From 60a811fd18aa9d7167ccd040869749a13c5fd7f7 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Mon, 30 Jun 2025 10:53:22 +0000 Subject: [PATCH] update --- readme.md | 263 ++++++++++- ts_web/elements/dees-button.demo.ts | 360 ++++++++++----- ts_web/elements/dees-form.demo.ts | 210 +++++++-- ts_web/elements/dees-input-datepicker.demo.ts | 421 ++++++++++++------ ts_web/elements/dees-input-datepicker.ts | 2 +- ts_web/elements/dees-input-dropdown.demo.ts | 220 ++++++--- ts_web/elements/dees-input-text.demo.ts | 272 ++++++++--- 7 files changed, 1318 insertions(+), 430 deletions(-) diff --git a/readme.md b/readme.md index 9b8acf1..167d79b 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ # @design.estate/dees-catalog -An extensive library for building modern web applications with dynamic components using Web Components, JavaScript, and TypeScript. +A comprehensive web components library built with TypeScript and LitElement, providing 75+ UI components for building modern web applications with consistent design and behavior. ## Install To install the `@design.estate/dees-catalog` library, you can use npm or any other compatible JavaScript package manager: @@ -12,15 +12,16 @@ npm install @design.estate/dees-catalog | Category | Components | |----------|------------| -| Core UI | `DeesButton`, `DeesButtonExit`, `DeesButtonGroup`, `DeesBadge`, `DeesChips`, `DeesHeading`, `DeesHint`, `DeesIcon`, `DeesLabel`, `DeesPanel`, `DeesSearchbar`, `DeesSpinner`, `DeesToast`, `DeesWindowcontrols` | -| Forms | `DeesForm`, `DeesInputText`, `DeesInputCheckbox`, `DeesInputDropdown`, `DeesInputRadiogroup`, `DeesInputFileupload`, `DeesInputIban`, `DeesInputPhone`, `DeesInputQuantitySelector`, `DeesInputMultitoggle`, `DeesInputTags`, `DeesInputTypelist`, `DeesInputRichtext`, `DeesInputWysiwyg`, `DeesFormSubmit` | -| Layout | `DeesAppuiBase`, `DeesAppuiMainmenu`, `DeesAppuiMainselector`, `DeesAppuiMaincontent`, `DeesAppuiAppbar`, `DeesAppuiActivitylog`, `DeesAppuiProfiledropdown`, `DeesAppuiTabs`, `DeesAppuiView`, `DeesMobileNavigation` | -| Data Display | `DeesTable`, `DeesDataviewCodebox`, `DeesDataviewStatusobject`, `DeesPdf`, `DeesStatsGrid`, `DeesPagination` | -| Visualization | `DeesChartArea`, `DeesChartLog` | -| Dialogs & Overlays | `DeesModal`, `DeesContextmenu`, `DeesSpeechbubble`, `DeesWindowlayer` | -| Navigation | `DeesStepper`, `DeesProgressbar`, `DeesMobileNavigation` | -| Development | `DeesEditor`, `DeesEditorMarkdown`, `DeesEditorMarkdownoutlet`, `DeesTerminal`, `DeesUpdater` | -| Auth & Utilities | `DeesSimpleAppdash`, `DeesSimpleLogin` | +| Core UI | [`DeesButton`](#deesbutton), [`DeesButtonExit`](#deesbuttonexit), [`DeesButtonGroup`](#deesbuttongroup), [`DeesBadge`](#deesbadge), [`DeesChips`](#deeschips), [`DeesHeading`](#deesheading), [`DeesHint`](#deeshint), [`DeesIcon`](#deesicon), [`DeesLabel`](#deeslabel), [`DeesPanel`](#deespanel), [`DeesSearchbar`](#deessearchbar), [`DeesSpinner`](#deesspinner), [`DeesToast`](#deestoast), [`DeesWindowcontrols`](#deeswindowcontrols) | +| Forms | [`DeesForm`](#deesform), [`DeesInputText`](#deesinputtext), [`DeesInputCheckbox`](#deesinputcheckbox), [`DeesInputDropdown`](#deesinputdropdown), [`DeesInputRadiogroup`](#deesinputradiogroup), [`DeesInputFileupload`](#deesinputfileupload), [`DeesInputIban`](#deesinputiban), [`DeesInputPhone`](#deesinputphone), [`DeesInputQuantitySelector`](#deesinputquantityselector), [`DeesInputMultitoggle`](#deesinputmultitoggle), [`DeesInputTags`](#deesinputtags), [`DeesInputTypelist`](#deesinputtypelist), [`DeesInputRichtext`](#deesinputrichtext), [`DeesInputWysiwyg`](#deesinputwysiwyg), [`DeesInputDatepicker`](#deesinputdatepicker), [`DeesInputSearchselect`](#deesinputsearchselect), [`DeesFormSubmit`](#deesformsubmit) | +| Layout | [`DeesAppuiBase`](#deesappuibase), [`DeesAppuiMainmenu`](#deesappuimainmenu), [`DeesAppuiMainselector`](#deesappuimainselector), [`DeesAppuiMaincontent`](#deesappuimaincontent), [`DeesAppuiAppbar`](#deesappuiappbar), [`DeesAppuiActivitylog`](#deesappuiactivitylog), [`DeesAppuiProfiledropdown`](#deesappuiprofiledropdown), [`DeesAppuiTabs`](#deesappuitabs), [`DeesAppuiView`](#deesappuiview), [`DeesMobileNavigation`](#deesmobilenavigation), [`DeesDashboardGrid`](#deesdashboardgrid) | +| Data Display | [`DeesTable`](#deestable), [`DeesDataviewCodebox`](#deesdataviewcodebox), [`DeesDataviewStatusobject`](#deesdataviewstatusobject), [`DeesPdf`](#deespdf), [`DeesStatsGrid`](#deesstatsgrid), [`DeesPagination`](#deespagination) | +| Visualization | [`DeesChartArea`](#deeschartarea), [`DeesChartLog`](#deeschartlog) | +| Dialogs & Overlays | [`DeesModal`](#deesmodal), [`DeesContextmenu`](#deescontextmenu), [`DeesSpeechbubble`](#deesspeechbubble), [`DeesWindowlayer`](#deeswindowlayer) | +| Navigation | [`DeesStepper`](#deesstepper), [`DeesProgressbar`](#deesprogressbar) | +| Development | [`DeesEditor`](#deeseditor), [`DeesEditorMarkdown`](#deeseditormarkdown), [`DeesEditorMarkdownoutlet`](#deeseditormarkdownoutlet), [`DeesTerminal`](#deesterminal), [`DeesUpdater`](#deesupdater) | +| Auth & Utilities | [`DeesSimpleAppdash`](#deessimpleappdash), [`DeesSimpleLogin`](#deessimplelogin) | +| Shopping | [`DeesShoppingProductcard`](#deesshoppingproductcard) | ## Detailed Component Documentation @@ -70,14 +71,36 @@ Interactive chips/tags with selection capabilities. ``` #### `DeesIcon` -Display icons from various icon sets including FontAwesome. +Display icons from FontAwesome and Lucide icon libraries with library prefixes. ```typescript +// FontAwesome icons - use 'fa:' prefix + +// Lucide icons - use 'lucide:' prefix + + +// Available FontAwesome icons include: +// fa:check, fa:bell, fa:gear, fa:trash, fa:copy, fa:paste, fa:eye, fa:eyeSlash, +// fa:plus, fa:minus, fa:circleInfo, fa:circleCheck, fa:circleXmark, fa:message, +// fa:arrowRight, fa:facebook, fa:twitter, fa:linkedin, fa:instagram, etc. + +// Available Lucide icons include: +// lucide:menu, lucide:settings, lucide:home, lucide:file, lucide:folder, +// lucide:search, lucide:user, lucide:heart, lucide:star, lucide:download, etc. + +// Legacy API (deprecated but still supported) + ``` @@ -431,6 +454,61 @@ Dynamic list input for managing arrays of typed values. > ``` +#### `DeesInputDatepicker` +Date and time picker component with calendar interface. + +```typescript + +``` + +Key Features: +- Interactive calendar popup +- Optional time selection +- Configurable date format +- Min/max date constraints +- Disable specific dates +- Keyboard navigation +- Today button +- Clear functionality +- 12/24 hour time formats +- Theme-aware styling + +#### `DeesInputSearchselect` +Search-enabled dropdown selection component. + +```typescript + +``` + #### `DeesInputRichtext` Rich text editor with formatting toolbar powered by TipTap. @@ -919,6 +997,100 @@ Responsive navigation component for mobile devices. > ``` +#### `DeesDashboardGrid` +Drag-and-drop grid layout system for creating customizable dashboards. + +```typescript +Widget content here`, + noMove: false, // Allow moving + noResize: false // Allow resizing + }, + { + id: 'widget2', + x: 4, + y: 0, + w: 4, + h: 3, + title: 'Recent Activity', + content: html``, + autoPosition: true // Auto-find position + } + ]} + columns={12} // Number of grid columns + cellHeight={80} // Height of each grid cell in pixels + cellHeightUnit="px" // Options: px, em, rem, auto + margin={10} // Gap between widgets + editable={true} // Enable drag and resize + showGridLines={false} // Show grid guidelines + enableAnimation={true} // Smooth transitions + rtl={false} // Right-to-left support + @widget-move=${handleWidgetMove} + @widget-resize=${handleWidgetResize} +> + +// Programmatic methods +const grid = document.querySelector('dees-dashboardgrid'); + +// Add a new widget +grid.addWidget({ + id: 'newWidget', + x: 0, + y: 0, + w: 3, + h: 2, + content: html`
New widget
` +}, true); // true = auto-position + +// Remove widget +grid.removeWidget('widget1'); + +// Update widget +grid.updateWidget('widget2', { + title: 'Updated Title', + w: 6 +}); + +// Get/set layout +const layout = grid.getLayout(); // Returns position data +grid.setLayout(savedLayout); // Restore positions + +// Compact widgets +grid.compact('vertical'); // Or 'horizontal' + +// Lock/unlock editing +grid.lockGrid(); +grid.unlockGrid(); +``` + +Key Features: +- Drag-and-drop widget repositioning +- Resize handles on edges and corners +- Grid-based layout system +- Collision detection +- Auto-positioning for new widgets +- Configurable constraints (min/max dimensions) +- Lock individual widgets or entire grid +- Compact layout algorithm +- Save/restore layout positions +- RTL layout support +- Optional grid lines for alignment +- Smooth animations +- Responsive sizing +- Empty state display + ### Data Display Components #### `DeesTable` @@ -1913,6 +2085,69 @@ Key Features: - Responsive layout - Loading states +### Shopping Components + +#### `DeesShoppingProductcard` +Product card component for e-commerce applications. + +```typescript + handleQuantityChange(e.detail)} + @selectionChange=${(e) => handleSelectionChange(e.detail)} +> +``` + +Key Features: +- Product image with fallback icon +- Category label +- Product name and description +- Price display with original price strikethrough +- Stock status indicator +- Built-in quantity selector +- Selection mode for bulk operations +- Hover effects +- Responsive design +- Theme-aware styling + +Product Data Interface: +```typescript +interface IProductData { + name: string; + category?: string; + description?: string; + price: number; + originalPrice?: number; + currency?: string; + inStock?: boolean; + stockText?: string; + imageUrl?: string; + iconName?: string; +} +``` + +Common Use Cases: +- Product listings +- Shopping carts +- Order summaries +- Product comparisons +- Wishlist displays + ## License and Legal Information This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the license file within this repository. diff --git a/ts_web/elements/dees-button.demo.ts b/ts_web/elements/dees-button.demo.ts index 373161d..4a090ab 100644 --- a/ts_web/elements/dees-button.demo.ts +++ b/ts_web/elements/dees-button.demo.ts @@ -1,83 +1,93 @@ -import { html, css, cssManager } from '@design.estate/dees-element'; +import { html, css, cssManager, domtools } from '@design.estate/dees-element'; import '@design.estate/dees-wcctools/demotools'; import './dees-panel.js'; import './dees-form.js'; import './dees-form-submit.js'; import './dees-input-text.js'; import './dees-icon.js'; +import type { DeesButton } from './dees-button.js'; export const demoFunc = () => html` - - - -
+ + +
+ { + // Log button clicks for demo purposes + const buttons = elementArg.querySelectorAll('dees-button'); + buttons.forEach((button) => { + button.addEventListener('clicked', () => { + const type = button.getAttribute('type') || 'default'; + console.log(`Button variant clicked: ${type}`); + }); + }); + }}>
Default @@ -88,7 +98,18 @@ export const demoFunc = () => html` Link Button
- +
+ + { + // Demonstrate size differences programmatically + const buttons = elementArg.querySelectorAll('dees-button'); + buttons.forEach((button) => { + button.addEventListener('clicked', () => { + const size = button.getAttribute('size') || 'default'; + console.log(`Button size: ${size}`); + }); + }); + }}>
Small Button @@ -103,7 +124,21 @@ export const demoFunc = () => html` Large Outline
- +
+ + { + // Track icon button clicks + const iconButtons = elementArg.querySelectorAll('dees-button'); + iconButtons.forEach((button) => { + button.addEventListener('clicked', () => { + const hasIcon = button.querySelector('dees-icon'); + if (hasIcon) { + const iconName = hasIcon.getAttribute('iconFA') || 'unknown'; + console.log(`Icon button clicked: ${iconName}`); + } + }); + }); + }}>
@@ -153,7 +188,33 @@ export const demoFunc = () => html`
+
+ + { + // Demonstrate status changes + const pendingButton = elementArg.querySelector('dees-button[status="pending"]'); + const successButton = elementArg.querySelector('dees-button[status="success"]'); + const errorButton = elementArg.querySelector('dees-button[status="error"]'); + // Simulate status changes + if (pendingButton) { + setTimeout(() => { + console.log('Pending button is showing loading state'); + }, 1000); + } + + if (successButton) { + successButton.addEventListener('clicked', () => { + console.log('Success state button clicked'); + }); + } + + if (errorButton) { + errorButton.addEventListener('clicked', () => { + console.log('Error state button clicked'); + }); + } + }}>
Normal @@ -169,61 +230,81 @@ export const demoFunc = () => html` Large Loading
+
+ + { + // Set up click handlers with the output element + const output = elementArg.querySelector('#click-output'); + const clickMeBtn = elementArg.querySelector('dees-button:first-of-type'); + const dataBtn = elementArg.querySelector('dees-button[type="secondary"]'); + const asyncBtn = elementArg.querySelector('dees-button[type="destructive"]'); + + if (clickMeBtn && output) { + clickMeBtn.addEventListener('clicked', () => { + output.textContent = `Clicked: Default button at ${new Date().toLocaleTimeString()}`; + }); + } + + if (dataBtn && output) { + dataBtn.addEventListener('clicked', (e: CustomEvent) => { + output.textContent = `Clicked: Secondary button with data: ${e.detail.data}`; + }); + } + + if (asyncBtn && output) { + asyncBtn.addEventListener('clicked', async () => { + output.textContent = 'Processing...'; + await domtools.plugins.smartdelay.delayFor(2000); + output.textContent = 'Action completed!'; + }); + } + }}>
- { - const output = document.querySelector('#click-output'); - if (output) { - output.textContent = `Clicked: Default button at ${new Date().toLocaleTimeString()}`; - } - }} - > - Click Me - - - { - const output = document.querySelector('#click-output'); - if (output) { - output.textContent = `Clicked: Secondary button with data: ${e.detail.data}`; - } - }} - > + Click Me + Click with Data - - { - const output = document.querySelector('#click-output'); - if (output) { - output.textContent = 'Processing...'; - await new Promise(resolve => setTimeout(resolve, 2000)); - output.textContent = 'Action completed!'; - } - }} - > - Async Action - + Async Action
Click a button to see the result...
+
+ + { + // Set up form submission handling + const form = elementArg.querySelector('dees-form'); + const output = elementArg.querySelector('#form-output'); + if (form && output) { + form.addEventListener('formData', (e: CustomEvent) => { + output.innerHTML = 'Form submitted with data:
' + + JSON.stringify(e.detail.data, null, 2); + }); + } + + // Track non-submit button clicks + const draftBtn = elementArg.querySelector('dees-button[type="secondary"]'); + const cancelBtn = elementArg.querySelector('dees-button[type="ghost"]'); + + if (draftBtn) { + draftBtn.addEventListener('clicked', () => { + console.log('Save Draft clicked'); + }); + } + + if (cancelBtn) { + cancelBtn.addEventListener('clicked', () => { + console.log('Cancel clicked'); + }); + } + }}> - { - const output = document.querySelector('#form-output'); - if (output) { - output.innerHTML = 'Form submitted with data:
' + - JSON.stringify(e.detail.data, null, 2); - } - }}> + @@ -237,7 +318,18 @@ export const demoFunc = () => html` Submit the form to see the data...
- + + + { + // Log legacy type mappings + const buttons = elementArg.querySelectorAll('dees-button'); + buttons.forEach((button) => { + const type = button.getAttribute('type'); + if (type) { + console.log(`Legacy type "${type}" is supported for backward compatibility`); + } + }); + }}>
Normal → Default @@ -250,7 +342,35 @@ export const demoFunc = () => html` These legacy type values are maintained for backward compatibility but we recommend using the new variant system.

+ + + { + // Track action group clicks + const actionGroup = elementArg.querySelectorAll('.vertical-group')[0]; + const dangerGroup = elementArg.querySelectorAll('.vertical-group')[1]; + if (actionGroup) { + const buttons = actionGroup.querySelectorAll('dees-button'); + buttons.forEach((button, index) => { + button.addEventListener('clicked', () => { + const action = ['Save Changes', 'Discard', 'Help'][index]; + console.log(`Action group: ${action} clicked`); + }); + }); + } + + if (dangerGroup) { + const buttons = dangerGroup.querySelectorAll('dees-button'); + buttons.forEach((button, index) => { + button.addEventListener('clicked', () => { + const action = ['Delete Account', 'Archive Data', 'Not Available'][index]; + if (index !== 2) { // Skip disabled button + console.log(`Danger zone: ${action} clicked`); + } + }); + }); + } + }}>
@@ -296,6 +416,6 @@ export const demoFunc = () => html`
-
-
-`; + +
+`; \ No newline at end of file diff --git a/ts_web/elements/dees-form.demo.ts b/ts_web/elements/dees-form.demo.ts index f674a80..ff5b788 100644 --- a/ts_web/elements/dees-form.demo.ts +++ b/ts_web/elements/dees-form.demo.ts @@ -3,40 +3,91 @@ import type { DeesForm } from './dees-form.js'; import '@design.estate/dees-wcctools/demotools'; export const demoFunc = () => html` - - + +
+ { + const form = elementArg.querySelector('dees-form') as DeesForm; + const outputDiv = elementArg.querySelector('.form-output'); + + if (form && outputDiv) { + form.addEventListener('formData', async (eventArg: CustomEvent) => { + const data = eventArg.detail.data; + console.log('Form submitted with data:', data); + + // Show processing state + form.setStatus('pending', 'Processing your registration...'); + outputDiv.innerHTML = `Submitted Data:\n${JSON.stringify(data, null, 2)}`; + + // Simulate API call + await domtools.plugins.smartdelay.delayFor(2000); + + // Show success + form.setStatus('success', 'Registration completed successfully!'); + + // Reset form after delay + await domtools.plugins.smartdelay.delayFor(2000); + form.reset(); + outputDiv.innerHTML = 'Form has been reset'; + }); - dees-panel { - margin-bottom: 24px; - } - - dees-panel:last-child { - margin-bottom: 0; - } - `} - - -
+ // Track individual field changes + const inputs = form.querySelectorAll('dees-input-text, dees-input-dropdown, dees-input-checkbox'); + inputs.forEach((input) => { + input.addEventListener('changeSubject', () => { + console.log('Field changed:', input.getAttribute('key')); + }); + }); + } + }}> - { - const form: DeesForm = eventArg.currentTarget; - form.setStatus('pending', 'Processing...'); - await domtools.plugins.smartdelay.delayFor(2000); - form.setStatus('success', 'Form submitted successfully!'); - await domtools.plugins.smartdelay.delayFor(2000); - form.reset(); - }} - > + html` Create Account + +
+ Submit the form to see the collected data... +
+ + + { + const form = elementArg.querySelector('dees-form') as DeesForm; + if (form) { + // Track horizontal layout behavior + console.log('Horizontal form layout active'); + + // Monitor filter changes + form.addEventListener('formData', (event: CustomEvent) => { + const filters = event.detail.data; + console.log('Filter applied:', filters); + + // Simulate search + const resultsCount = Math.floor(Math.random() * 100) + 1; + console.log(`Found ${resultsCount} results with filters:`, filters); + }); + + // Setup real-time filter updates + const inputs = form.querySelectorAll('[key]'); + inputs.forEach((input) => { + input.addEventListener('changeSubject', async () => { + // Get current form data + const formData = await form.collectFormData(); + console.log('Live filter update:', formData); + }); + }); + } + }}> html` > + + + { + const form = elementArg.querySelector('dees-form') as DeesForm; + const statusDiv = elementArg.querySelector('#status-display'); + if (form) { + form.addEventListener('formData', async (eventArg: CustomEvent) => { + const data = eventArg.detail.data; + console.log('Advanced form data:', data); + + // Show validation in progress + form.setStatus('pending', 'Validating your information...'); + + // Simulate validation + await domtools.plugins.smartdelay.delayFor(1500); + + // Check IBAN validity (simple check) + if (data.iban && data.iban.length > 15) { + form.setStatus('success', 'Application submitted successfully!'); + + if (statusDiv) { + statusDiv.className = 'status-message success'; + statusDiv.textContent = '✓ Your application has been submitted. We will contact you soon.'; + } + } else { + form.setStatus('error', 'Please check your IBAN'); + + if (statusDiv) { + statusDiv.className = 'status-message error'; + statusDiv.textContent = '✗ Invalid IBAN format. Please check and try again.'; + } + } + + console.log('Form data logged:', data); + }); + + // Monitor file uploads + const fileUpload = form.querySelector('dees-input-fileupload'); + if (fileUpload) { + fileUpload.addEventListener('change', (event: any) => { + const files = event.detail?.files || []; + console.log(`${files.length} file(s) selected for upload`); + }); + } + } + }}> - { - const form: DeesForm = eventArg.currentTarget; - const data = eventArg.detail.data; - console.log('Form data:', data); - form.setStatus('success', 'Data logged to console!'); - }} - > + html` Submit Application + +
-
-
+ +
`; \ No newline at end of file diff --git a/ts_web/elements/dees-input-datepicker.demo.ts b/ts_web/elements/dees-input-datepicker.demo.ts index 83853e5..558a097 100644 --- a/ts_web/elements/dees-input-datepicker.demo.ts +++ b/ts_web/elements/dees-input-datepicker.demo.ts @@ -1,146 +1,307 @@ -import { html } from '@design.estate/dees-element'; +import { html, css } from '@design.estate/dees-element'; +import '@design.estate/dees-wcctools/demotools'; +import './dees-panel.js'; import './dees-input-datepicker.js'; +import type { DeesInputDatepicker } from './dees-input-datepicker.js'; export const demoFunc = () => html`
-
-
Basic Date Picker
-
Simple date selection without time
- -
+ { + // Demonstrate basic date picker functionality + const datePicker = elementArg.querySelector('dees-input-datepicker'); + + if (datePicker) { + datePicker.addEventListener('change', (event: CustomEvent) => { + console.log('Basic date selected:', (event.target as DeesInputDatepicker).value); + }); + } + }}> + + + + -
-
Date and Time Picker
-
Date selection with time in 24-hour format
- -
+ { + // Demonstrate date and time picker + const dateTimePicker = elementArg.querySelector('dees-input-datepicker[label="Event Date & Time"]'); + const appointmentPicker = elementArg.querySelector('dees-input-datepicker[label="Appointment"]'); + + if (dateTimePicker) { + dateTimePicker.addEventListener('change', (event: CustomEvent) => { + const value = (event.target as DeesInputDatepicker).value; + console.log('24h format datetime:', value); + }); + } + + if (appointmentPicker) { + appointmentPicker.addEventListener('change', (event: CustomEvent) => { + const value = (event.target as DeesInputDatepicker).value; + console.log('12h format datetime:', value); + }); + } + }}> + + + + + + -
-
12-Hour Time Format
-
Date and time with AM/PM selector
- -
+ { + // Demonstrate date constraints + const futureDatePicker = elementArg.querySelector('dees-input-datepicker'); + + if (futureDatePicker) { + // Show the min/max constraints in action + futureDatePicker.addEventListener('change', (event: CustomEvent) => { + const value = (event.target as DeesInputDatepicker).value; + if (value) { + const selectedDate = new Date(value); + const today = new Date(); + const daysDiff = Math.floor((selectedDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)); + console.log(`Selected date is ${daysDiff} days from today`); + } + }); + } + }}> + + + + -
-
Date Range Constraints
-
Limit selectable dates with min and max
- -
+ { + // Demonstrate different date formats + const formatters = { + 'DD/MM/YYYY': 'European', + 'MM/DD/YYYY': 'US', + 'YYYY-MM-DD': 'ISO' + }; + + const datePickers = elementArg.querySelectorAll('dees-input-datepicker'); + datePickers.forEach((picker) => { + picker.addEventListener('change', (event: CustomEvent) => { + const target = event.target as DeesInputDatepicker; + // Log the formatted value that's displayed in the input + const input = target.shadowRoot?.querySelector('.date-input') as HTMLInputElement; + if (input) { + console.log(`${target.label} format:`, input.value); + } + }); + }); + }}> + +
+ + + + + +
+
+
-
-
Custom Date Format
-
Different date display format
- -
+ { + // Demonstrate required field validation + const requiredPicker = elementArg.querySelector('dees-input-datepicker[required]'); + + if (requiredPicker) { + // Monitor blur events for validation + requiredPicker.addEventListener('blur', () => { + const picker = requiredPicker as DeesInputDatepicker; + const value = picker.getValue(); + if (!value) { + console.log('Required date field is empty'); + } + }); + } + }}> + + + + + + -
-
Required Field
-
Date picker as a required form field
- -
+ { + // Demonstrate week start customization + const usPicker = elementArg.querySelector('dees-input-datepicker[label="US Calendar"]'); + const euPicker = elementArg.querySelector('dees-input-datepicker[label="EU Calendar"]'); + + if (usPicker) { + console.log('US Calendar starts on Sunday (0)'); + } + if (euPicker) { + console.log('EU Calendar starts on Monday (1)'); + } + }}> + +
+ + + +
+
+
-
-
Disabled State
-
Date picker in disabled state
- -
+ { + // Generate weekend dates for the current month + const generateWeekends = () => { + const weekends = []; + const now = new Date(); + const year = now.getFullYear(); + const month = now.getMonth(); + + // Get all weekends for current month + const date = new Date(year, month, 1); + while (date.getMonth() === month) { + if (date.getDay() === 0 || date.getDay() === 6) { + weekends.push(new Date(date).toISOString()); + } + date.setDate(date.getDate() + 1); + } + return weekends; + }; + + const picker = elementArg.querySelector('dees-input-datepicker'); + if (picker) { + picker.disabledDates = generateWeekends(); + console.log('Disabled weekend dates for current month'); + } + }}> + + + + -
-
Week Starts on Sunday
-
Calendar with Sunday as first day of week
- -
- -
-
With Disabled Dates
-
Some dates are disabled and cannot be selected
- -
- -
-
Event Listeners
-
Check console for change events
- -
+ { + // Interactive event demonstration + const picker = elementArg.querySelector('dees-input-datepicker'); + const output = elementArg.querySelector('#event-output'); + + if (picker && output) { + picker.addEventListener('change', (event: CustomEvent) => { + const target = event.target as DeesInputDatepicker; + const value = target.value; + if (value) { + const date = new Date(value); + // Get the formatted value from the input element + const input = target.shadowRoot?.querySelector('.date-input') as HTMLInputElement; + const formattedValue = input?.value || 'N/A'; + output.innerHTML = ` + Event triggered!
+ ISO Value: ${value}
+ Formatted: ${formattedValue}
+ Date object: ${date.toLocaleString()} + `; + } else { + output.innerHTML = 'Date cleared'; + } + }); + + picker.addEventListener('blur', () => { + console.log('Datepicker lost focus'); + }); + } + }}> + + + +
+ Select a date to see event details... +
+
+
`; \ No newline at end of file diff --git a/ts_web/elements/dees-input-datepicker.ts b/ts_web/elements/dees-input-datepicker.ts index 059c76b..b3fd796 100644 --- a/ts_web/elements/dees-input-datepicker.ts +++ b/ts_web/elements/dees-input-datepicker.ts @@ -627,7 +627,7 @@ export class DeesInputDatepicker extends DeesInputBase { } } - private formatDate(isoString: string): string { + public formatDate(isoString: string): string { if (!isoString) return ''; try { diff --git a/ts_web/elements/dees-input-dropdown.demo.ts b/ts_web/elements/dees-input-dropdown.demo.ts index d61e36c..4cf6bd7 100644 --- a/ts_web/elements/dees-input-dropdown.demo.ts +++ b/ts_web/elements/dees-input-dropdown.demo.ts @@ -5,45 +5,63 @@ import './dees-form.js'; import './dees-form-submit.js'; export const demoFunc = () => html` - - - -
+ + +
+ { + // Demonstrate programmatic interaction with basic dropdowns + const countryDropdown = elementArg.querySelector('dees-input-dropdown[label="Select Country"]'); + const roleDropdown = elementArg.querySelector('dees-input-dropdown[label="Select Role"]'); + + // Log when country changes + if (countryDropdown) { + countryDropdown.addEventListener('selectedOption', (event: CustomEvent) => { + console.log('Country selected:', event.detail); + }); + } + + // Log when role changes + if (roleDropdown) { + roleDropdown.addEventListener('selectedOption', (event: CustomEvent) => { + console.log('Role selected:', event.detail); + }); + } + }}> html` ]} > + + + { + // Demonstrate simpler dropdown without search + const priorityDropdown = elementArg.querySelector('dees-input-dropdown'); + if (priorityDropdown) { + priorityDropdown.addEventListener('selectedOption', (event: CustomEvent) => { + console.log(`Priority changed to: ${event.detail.option}`); + }); + } + }}> html` .selectedOption=${{ option: 'Medium', key: 'medium' }} > + + + { + // Demonstrate horizontal layout with multiple dropdowns + const dropdowns = elementArg.querySelectorAll('dees-input-dropdown'); + // Log all changes from horizontal dropdowns + dropdowns.forEach((dropdown) => { + dropdown.addEventListener('selectedOption', (event: CustomEvent) => { + const label = dropdown.getAttribute('label'); + console.log(`${label}: ${event.detail.option}`); + }); + }); + }}>
html` >
+
+ + { + // Demonstrate state handling + const requiredDropdown = elementArg.querySelector('dees-input-dropdown[required]'); + if (requiredDropdown) { + // Show validation state changes + requiredDropdown.addEventListener('blur', () => { + console.log('Required dropdown lost focus'); + }); + } + }}> html` .selectedOption=${{ option: 'Cannot Select', key: 'disabled' }} > + + +
+ (Spacer to test dropdown positioning) +
+ + { + // This dropdown demonstrates automatic positioning + const dropdown = elementArg.querySelector('dees-input-dropdown'); -
- (Spacer to test dropdown positioning) -
- + if (dropdown) { + dropdown.addEventListener('selectedOption', (event: CustomEvent) => { + console.log('Bottom dropdown selected:', event.detail); + }); + + // Note: The dropdown automatically detects available space + // and opens upward when near the bottom of the viewport + } + }}> html` ]} > +
+ { + // Setup the interactive payload display + const dropdown = elementArg.querySelector('dees-input-dropdown'); + const output = elementArg.querySelector('#selection-output'); + + if (dropdown && output) { + // Initialize output + output.innerHTML = 'Select a product to see details...'; + + // Handle dropdown changes + dropdown.addEventListener('change', (event: CustomEvent) => { + if (event.detail.value) { + output.innerHTML = ` + Selected: ${event.detail.value.option}
+ Key: ${event.detail.value.key}
+ Price: $${event.detail.value.payload?.price || 'N/A'}
+ Features: ${event.detail.value.payload?.features?.join(', ') || 'N/A'} + `; + } + }); + } + }}> html` { option: 'Pro Plan', key: 'pro', payload: { price: 19.99, features: ['Feature A', 'Feature B'] } }, { option: 'Enterprise Plan', key: 'enterprise', payload: { price: 49.99, features: ['Feature A', 'Feature B', 'Feature C'] } } ]} - @change=${(e: CustomEvent) => { - const output = document.querySelector('#selection-output'); - if (output && e.detail.value) { - output.innerHTML = ` - Selected: ${e.detail.value.option}
- Key: ${e.detail.value.key}
- Price: $${e.detail.value.payload?.price || 'N/A'}
- Features: ${e.detail.value.payload?.features?.join(', ') || 'N/A'} - `; - } - }} >
-
- Select a product to see details... -
+
+
+ { + // Demonstrate form integration and validation + const form = elementArg.querySelector('dees-form'); + const projectTypeDropdown = elementArg.querySelector('dees-input-dropdown[key="projectType"]'); + const frameworkDropdown = elementArg.querySelector('dees-input-dropdown[key="framework"]'); + + if (form) { + form.addEventListener('formData', (event: CustomEvent) => { + console.log('Form submitted with data:', event.detail.data); + }); + } + + if (projectTypeDropdown && frameworkDropdown) { + // Filter frameworks based on project type + projectTypeDropdown.addEventListener('selectedOption', (event: CustomEvent) => { + const selectedType = event.detail.key; + console.log(`Project type changed to: ${selectedType}`); + + // In a real app, you could filter the framework options based on project type + // For demo purposes, we just log the change + }); + } + }}> html` -
- + +
` \ No newline at end of file diff --git a/ts_web/elements/dees-input-text.demo.ts b/ts_web/elements/dees-input-text.demo.ts index e1cfc36..7b936e5 100644 --- a/ts_web/elements/dees-input-text.demo.ts +++ b/ts_web/elements/dees-input-text.demo.ts @@ -1,67 +1,87 @@ import { html, css, cssManager } from '@design.estate/dees-element'; import '@design.estate/dees-wcctools/demotools'; import './dees-panel.js'; +import type { DeesInputText } from './dees-input-text.js'; export const demoFunc = () => html` - - + +
+ { + // Demonstrate basic text input functionality + const inputs = elementArg.querySelectorAll('dees-input-text'); + + inputs.forEach((input: DeesInputText) => { + input.addEventListener('changeSubject', (event: CustomEvent) => { + console.log(`Input "${input.label}" changed to:`, input.getValue()); + }); - @media (max-width: 768px) { - .grid-layout { - grid-template-columns: 1fr; - } - } - - .interactive-section { - background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')}; - border-radius: 8px; - padding: 16px; - margin-top: 16px; - } - - .output-text { - font-family: monospace; - font-size: 13px; - color: ${cssManager.bdTheme('hsl(215.3 25% 26.7%)', 'hsl(210 40% 80%)')}; - padding: 8px; - background: ${cssManager.bdTheme('hsl(210 40% 98%)', 'hsl(215 20.2% 11.8%)')}; - border-radius: 4px; - min-height: 24px; - } - `} - - -
+ input.addEventListener('blur', () => { + console.log(`Input "${input.label}" lost focus`); + }); + }); + + // Show password visibility toggle + const passwordInput = elementArg.querySelector('dees-input-text[key="password"]') as DeesInputText; + if (passwordInput) { + console.log('Password input includes visibility toggle'); + } + }}> html` .key=${'password'} > + + + { + // Demonstrate horizontal layout behavior + const horizontalInputs = elementArg.querySelectorAll('dees-input-text'); + // Check that inputs are properly spaced horizontally + horizontalInputs.forEach((input: DeesInputText) => { + const computedStyle = window.getComputedStyle(input); + console.log(`Horizontal input "${input.label}" display:`, computedStyle.display); + }); + + // Track value changes + const firstNameInput = elementArg.querySelector('dees-input-text[key="firstName"]'); + const lastNameInput = elementArg.querySelector('dees-input-text[key="lastName"]'); + + if (firstNameInput && lastNameInput) { + const updateFullName = () => { + const firstName = (firstNameInput as DeesInputText).getValue(); + const lastName = (lastNameInput as DeesInputText).getValue(); + console.log(`Full name: ${firstName} ${lastName}`); + }; + + firstNameInput.addEventListener('changeSubject', updateFullName); + lastNameInput.addEventListener('changeSubject', updateFullName); + } + }}>
html` >
+
+ + { + // Demonstrate different label positions + const inputs = elementArg.querySelectorAll('dees-input-text'); + inputs.forEach((input: DeesInputText) => { + const position = input.labelPosition; + console.log(`Input "${input.label}" has label position: ${position}`); + }); + + // Show how label position affects layout + const leftLabelInputs = elementArg.querySelectorAll('dees-input-text[labelPosition="left"]'); + if (leftLabelInputs.length > 0) { + console.log(`${leftLabelInputs.length} inputs have left-aligned labels for inline layout`); + } + }}> html` >
+
+ + { + // Demonstrate validation states + const requiredInput = elementArg.querySelector('dees-input-text[required]') as DeesInputText; + const disabledInput = elementArg.querySelector('dees-input-text[disabled]') as DeesInputText; + const errorInput = elementArg.querySelector('dees-input-text[validationState="invalid"]') as DeesInputText; + if (requiredInput) { + // Show validation on blur for empty required field + requiredInput.addEventListener('blur', () => { + if (!requiredInput.getValue()) { + console.log('Required field is empty!'); + } + }); + } + + if (disabledInput) { + console.log('Disabled input cannot be edited'); + } + + if (errorInput) { + console.log('Error input shows validation message:', errorInput.validationText); + + // Simulate fixing the error + errorInput.addEventListener('changeSubject', () => { + const value = errorInput.getValue(); + if (value.includes('@') && value.includes('.')) { + errorInput.validationState = 'valid'; + errorInput.validationText = ''; + console.log('Email validation passed!'); + } + }); + } + }}> html` .validationState=${'invalid'} > + + + { + // Track password visibility toggles + const passwordInputs = elementArg.querySelectorAll('dees-input-text[isPasswordBool]'); + passwordInputs.forEach((input: DeesInputText) => { + // Monitor for toggle button clicks within shadow DOM + const checkToggle = () => { + const inputEl = input.shadowRoot?.querySelector('input'); + if (inputEl) { + console.log(`Password field "${input.label}" type:`, inputEl.type); + } + }; + + // Use MutationObserver to detect changes + if (input.shadowRoot) { + const observer = new MutationObserver(checkToggle); + const inputEl = input.shadowRoot.querySelector('input'); + if (inputEl) { + observer.observe(inputEl, { attributes: true, attributeFilter: ['type'] }); + } + } + }); + }}> html` .description=${'Keep this key secure and never share it'} > + + + { + // Set up interactive example + const dynamicInput = elementArg.querySelector('dees-input-text'); + const output = elementArg.querySelector('#text-input-output'); + if (dynamicInput && output) { + // Update output on every change + dynamicInput.addEventListener('changeSubject', (event: CustomEvent) => { + const value = (event.detail as DeesInputText).getValue(); + output.textContent = `Current value: "${value}"`; + }); + + // Also track focus/blur events + dynamicInput.addEventListener('focus', () => { + console.log('Input focused'); + }); + + dynamicInput.addEventListener('blur', () => { + console.log('Input blurred'); + }); + + // Track keypress events + let keypressCount = 0; + dynamicInput.addEventListener('keydown', () => { + keypressCount++; + console.log(`Keypress count: ${keypressCount}`); + }); + } + }}> { - const output = document.querySelector('#text-input-output'); - if (output && event.detail) { - output.textContent = `Current value: "${event.detail.getValue()}"`; - } - }} >
Current value: ""
-
-
+
+ `; \ No newline at end of file