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`);
+ }
+ });
+ }}>
-
-`;
+
+
+`;
\ 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
-
console.log('Date changed:', (e.target as any).value)}"
- >
-
+
{
+ // 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()}"`;
- }
- }}
>
-
-
+
+
`;
\ No newline at end of file