This commit is contained in:
Juergen Kunz
2025-06-30 10:53:22 +00:00
parent 9a9aea56da
commit 60a811fd18
7 changed files with 1318 additions and 430 deletions

263
readme.md
View File

@ -1,5 +1,5 @@
# @design.estate/dees-catalog # @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 ## Install
To install the `@design.estate/dees-catalog` library, you can use npm or any other compatible JavaScript package manager: 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 | | Category | Components |
|----------|------------| |----------|------------|
| Core UI | `DeesButton`, `DeesButtonExit`, `DeesButtonGroup`, `DeesBadge`, `DeesChips`, `DeesHeading`, `DeesHint`, `DeesIcon`, `DeesLabel`, `DeesPanel`, `DeesSearchbar`, `DeesSpinner`, `DeesToast`, `DeesWindowcontrols` | | 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`, `DeesInputText`, `DeesInputCheckbox`, `DeesInputDropdown`, `DeesInputRadiogroup`, `DeesInputFileupload`, `DeesInputIban`, `DeesInputPhone`, `DeesInputQuantitySelector`, `DeesInputMultitoggle`, `DeesInputTags`, `DeesInputTypelist`, `DeesInputRichtext`, `DeesInputWysiwyg`, `DeesFormSubmit` | | 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`, `DeesAppuiMainmenu`, `DeesAppuiMainselector`, `DeesAppuiMaincontent`, `DeesAppuiAppbar`, `DeesAppuiActivitylog`, `DeesAppuiProfiledropdown`, `DeesAppuiTabs`, `DeesAppuiView`, `DeesMobileNavigation` | | 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`, `DeesDataviewCodebox`, `DeesDataviewStatusobject`, `DeesPdf`, `DeesStatsGrid`, `DeesPagination` | | Data Display | [`DeesTable`](#deestable), [`DeesDataviewCodebox`](#deesdataviewcodebox), [`DeesDataviewStatusobject`](#deesdataviewstatusobject), [`DeesPdf`](#deespdf), [`DeesStatsGrid`](#deesstatsgrid), [`DeesPagination`](#deespagination) |
| Visualization | `DeesChartArea`, `DeesChartLog` | | Visualization | [`DeesChartArea`](#deeschartarea), [`DeesChartLog`](#deeschartlog) |
| Dialogs & Overlays | `DeesModal`, `DeesContextmenu`, `DeesSpeechbubble`, `DeesWindowlayer` | | Dialogs & Overlays | [`DeesModal`](#deesmodal), [`DeesContextmenu`](#deescontextmenu), [`DeesSpeechbubble`](#deesspeechbubble), [`DeesWindowlayer`](#deeswindowlayer) |
| Navigation | `DeesStepper`, `DeesProgressbar`, `DeesMobileNavigation` | | Navigation | [`DeesStepper`](#deesstepper), [`DeesProgressbar`](#deesprogressbar) |
| Development | `DeesEditor`, `DeesEditorMarkdown`, `DeesEditorMarkdownoutlet`, `DeesTerminal`, `DeesUpdater` | | Development | [`DeesEditor`](#deeseditor), [`DeesEditorMarkdown`](#deeseditormarkdown), [`DeesEditorMarkdownoutlet`](#deeseditormarkdownoutlet), [`DeesTerminal`](#deesterminal), [`DeesUpdater`](#deesupdater) |
| Auth & Utilities | `DeesSimpleAppdash`, `DeesSimpleLogin` | | Auth & Utilities | [`DeesSimpleAppdash`](#deessimpleappdash), [`DeesSimpleLogin`](#deessimplelogin) |
| Shopping | [`DeesShoppingProductcard`](#deesshoppingproductcard) |
## Detailed Component Documentation ## Detailed Component Documentation
@ -70,14 +71,36 @@ Interactive chips/tags with selection capabilities.
``` ```
#### `DeesIcon` #### `DeesIcon`
Display icons from various icon sets including FontAwesome. Display icons from FontAwesome and Lucide icon libraries with library prefixes.
```typescript ```typescript
// FontAwesome icons - use 'fa:' prefix
<dees-icon <dees-icon
icon="home" // FontAwesome icon name icon="fa:check" // FontAwesome icon with fa: prefix
type="solid" // Options: solid, regular, brands iconSize="24" // Size in pixels
size="1.5rem" // Optional: custom size color="#22c55e" // Optional: custom color
></dees-icon>
// Lucide icons - use 'lucide:' prefix
<dees-icon
icon="lucide:menu" // Lucide icon with lucide: prefix
iconSize="24" // Size in pixels
color="#007bff" // Optional: custom color color="#007bff" // Optional: custom color
strokeWidth="2" // Optional: stroke width for Lucide icons
></dees-icon>
// 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)
<dees-icon
iconFA="check" // Without prefix - assumes FontAwesome
></dees-icon> ></dees-icon>
``` ```
@ -431,6 +454,61 @@ Dynamic list input for managing arrays of typed values.
></dees-input-typelist> ></dees-input-typelist>
``` ```
#### `DeesInputDatepicker`
Date and time picker component with calendar interface.
```typescript
<dees-input-datepicker
key="eventDate"
label="Event Date"
placeholder="Select date"
value="2025-01-15T14:30:00Z" // ISO string format
dateFormat="DD/MM/YYYY" // Display format
enableTime={true} // Enable time selection
timeFormat="24h" // Options: 24h, 12h
minuteIncrement={15} // Time step in minutes
minDate="2025-01-01" // Minimum selectable date
maxDate="2025-12-31" // Maximum selectable date
.disabledDates=${[ // Array of disabled dates
'2025-01-10',
'2025-01-11'
]}
weekStartsOn={1} // 0 = Sunday, 1 = Monday
required
@change=${handleDateChange}
></dees-input-datepicker>
```
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
<dees-input-searchselect
key="category"
label="Select Category"
placeholder="Search categories..."
.options=${[
{ key: 'tech', label: 'Technology' },
{ key: 'health', label: 'Healthcare' },
{ key: 'finance', label: 'Finance' }
]}
required
@change=${handleCategoryChange}
></dees-input-searchselect>
```
#### `DeesInputRichtext` #### `DeesInputRichtext`
Rich text editor with formatting toolbar powered by TipTap. Rich text editor with formatting toolbar powered by TipTap.
@ -919,6 +997,100 @@ Responsive navigation component for mobile devices.
></dees-mobile-navigation> ></dees-mobile-navigation>
``` ```
#### `DeesDashboardGrid`
Drag-and-drop grid layout system for creating customizable dashboards.
```typescript
<dees-dashboardgrid
.widgets=${[
{
id: 'widget1',
x: 0, // Grid column position
y: 0, // Grid row position
w: 4, // Width in grid units
h: 3, // Height in grid units
minW: 2, // Minimum width
minH: 2, // Minimum height
maxW: 6, // Maximum width
title: 'Sales Overview',
icon: 'fa:chart-line',
content: html`<div>Widget content here</div>`,
noMove: false, // Allow moving
noResize: false // Allow resizing
},
{
id: 'widget2',
x: 4,
y: 0,
w: 4,
h: 3,
title: 'Recent Activity',
content: html`<dees-table .data=${activityData}></dees-table>`,
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}
></dees-dashboardgrid>
// 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`<div>New widget</div>`
}, 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 ### Data Display Components
#### `DeesTable` #### `DeesTable`
@ -1913,6 +2085,69 @@ Key Features:
- Responsive layout - Responsive layout
- Loading states - Loading states
### Shopping Components
#### `DeesShoppingProductcard`
Product card component for e-commerce applications.
```typescript
<dees-shopping-productcard
.productData=${{
name: 'Premium Headphones',
category: 'Electronics',
description: 'High-quality wireless headphones with noise cancellation',
price: 199.99,
originalPrice: 249.99, // Shows strikethrough price
currency: '$',
inStock: true,
stockText: 'In Stock', // Custom stock text
imageUrl: '/images/headphones.jpg',
iconName: 'lucide:headphones' // Fallback icon if no image
}}
quantity={1} // Current quantity
showQuantitySelector={true} // Show quantity selector
selectable={false} // Enable selection mode
selected={false} // Selection state
@quantityChange=${(e) => handleQuantityChange(e.detail)}
@selectionChange=${(e) => handleSelectionChange(e.detail)}
></dees-shopping-productcard>
```
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 ## 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. 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.

View File

@ -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 '@design.estate/dees-wcctools/demotools';
import './dees-panel.js'; import './dees-panel.js';
import './dees-form.js'; import './dees-form.js';
import './dees-form-submit.js'; import './dees-form-submit.js';
import './dees-input-text.js'; import './dees-input-text.js';
import './dees-icon.js'; import './dees-icon.js';
import type { DeesButton } from './dees-button.js';
export const demoFunc = () => html` export const demoFunc = () => html`
<dees-demowrapper> <style>
<style> ${css`
${css` .demo-container {
.demo-container { display: flex;
display: flex; flex-direction: column;
flex-direction: column; gap: 24px;
gap: 24px; padding: 24px;
padding: 24px; max-width: 1200px;
max-width: 1200px; margin: 0 auto;
margin: 0 auto; }
}
dees-panel { dees-panel {
margin-bottom: 24px; margin-bottom: 24px;
} }
dees-panel:last-child { dees-panel:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
.button-group { .button-group {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.vertical-group { .vertical-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
max-width: 300px; max-width: 300px;
} }
.horizontal-group { .horizontal-group {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: 16px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.demo-output { .demo-output {
margin-top: 16px; margin-top: 16px;
padding: 12px; padding: 12px;
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')}; background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')};
border-radius: 6px; border-radius: 6px;
font-size: 14px; font-size: 14px;
font-family: monospace; font-family: monospace;
color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')}; color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')};
} }
.icon-row { .icon-row {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
margin: 8px 0; margin: 8px 0;
} }
.code-snippet { .code-snippet {
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 11.8%)')}; background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 11.8%)')};
padding: 8px 12px; padding: 8px 12px;
border-radius: 4px; border-radius: 4px;
font-family: monospace; font-family: monospace;
font-size: 13px; font-size: 13px;
display: inline-block; display: inline-block;
margin: 4px 0; margin: 4px 0;
} }
`} `}
</style> </style>
<div class="demo-container"> <div class="demo-container">
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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}`);
});
});
}}>
<dees-panel .title=${'1. Button Variants'} .subtitle=${'Different visual styles for various use cases'}> <dees-panel .title=${'1. Button Variants'} .subtitle=${'Different visual styles for various use cases'}>
<div class="button-group"> <div class="button-group">
<dees-button type="default">Default</dees-button> <dees-button type="default">Default</dees-button>
@ -88,7 +98,18 @@ export const demoFunc = () => html`
<dees-button type="link">Link Button</dees-button> <dees-button type="link">Link Button</dees-button>
</div> </div>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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}`);
});
});
}}>
<dees-panel .title=${'2. Button Sizes'} .subtitle=${'Multiple sizes for different contexts and use cases'}> <dees-panel .title=${'2. Button Sizes'} .subtitle=${'Multiple sizes for different contexts and use cases'}>
<div class="button-group"> <div class="button-group">
<dees-button size="sm">Small Button</dees-button> <dees-button size="sm">Small Button</dees-button>
@ -103,7 +124,21 @@ export const demoFunc = () => html`
<dees-button size="lg" type="outline">Large Outline</dees-button> <dees-button size="lg" type="outline">Large Outline</dees-button>
</div> </div>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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}`);
}
});
});
}}>
<dees-panel .title=${'3. Buttons with Icons'} .subtitle=${'Combining icons with text for enhanced visual communication'}> <dees-panel .title=${'3. Buttons with Icons'} .subtitle=${'Combining icons with text for enhanced visual communication'}>
<div class="icon-row"> <div class="icon-row">
<dees-button> <dees-button>
@ -153,7 +188,33 @@ export const demoFunc = () => html`
</dees-button> </dees-button>
</div> </div>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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');
});
}
}}>
<dees-panel .title=${'4. Button States'} .subtitle=${'Different states to indicate button status and loading conditions'}> <dees-panel .title=${'4. Button States'} .subtitle=${'Different states to indicate button status and loading conditions'}>
<div class="button-group"> <div class="button-group">
<dees-button status="normal">Normal</dees-button> <dees-button status="normal">Normal</dees-button>
@ -169,61 +230,81 @@ export const demoFunc = () => html`
<dees-button type="destructive" status="pending" size="lg">Large Loading</dees-button> <dees-button type="destructive" status="pending" size="lg">Large Loading</dees-button>
</div> </div>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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!';
});
}
}}>
<dees-panel .title=${'5. Event Handling'} .subtitle=${'Interactive examples with click event handling'}> <dees-panel .title=${'5. Event Handling'} .subtitle=${'Interactive examples with click event handling'}>
<div class="button-group"> <div class="button-group">
<dees-button <dees-button>Click Me</dees-button>
@clicked=${() => { <dees-button type="secondary" .eventDetailData=${'custom-data-123'}>
const output = document.querySelector('#click-output');
if (output) {
output.textContent = `Clicked: Default button at ${new Date().toLocaleTimeString()}`;
}
}}
>
Click Me
</dees-button>
<dees-button
type="secondary"
.eventDetailData=${'custom-data-123'}
@clicked=${(e: CustomEvent) => {
const output = document.querySelector('#click-output');
if (output) {
output.textContent = `Clicked: Secondary button with data: ${e.detail.data}`;
}
}}
>
Click with Data Click with Data
</dees-button> </dees-button>
<dees-button type="destructive">Async Action</dees-button>
<dees-button
type="destructive"
@clicked=${async () => {
const output = document.querySelector('#click-output');
if (output) {
output.textContent = 'Processing...';
await new Promise(resolve => setTimeout(resolve, 2000));
output.textContent = 'Action completed!';
}
}}
>
Async Action
</dees-button>
</div> </div>
<div id="click-output" class="demo-output"> <div id="click-output" class="demo-output">
<em>Click a button to see the result...</em> <em>Click a button to see the result...</em>
</div> </div>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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 = '<strong>Form submitted with data:</strong><br>' +
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');
});
}
}}>
<dees-panel .title=${'6. Form Integration'} .subtitle=${'Buttons working within forms with automatic spacing'}> <dees-panel .title=${'6. Form Integration'} .subtitle=${'Buttons working within forms with automatic spacing'}>
<dees-form @formData=${(e: CustomEvent) => { <dees-form>
const output = document.querySelector('#form-output');
if (output) {
output.innerHTML = '<strong>Form submitted with data:</strong><br>' +
JSON.stringify(e.detail.data, null, 2);
}
}}>
<dees-input-text label="Name" key="name" required></dees-input-text> <dees-input-text label="Name" key="name" required></dees-input-text>
<dees-input-text label="Email" key="email" type="email" required></dees-input-text> <dees-input-text label="Email" key="email" type="email" required></dees-input-text>
<dees-input-text label="Message" key="message" isMultiline></dees-input-text> <dees-input-text label="Message" key="message" isMultiline></dees-input-text>
@ -237,7 +318,18 @@ export const demoFunc = () => html`
<em>Submit the form to see the data...</em> <em>Submit the form to see the data...</em>
</div> </div>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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`);
}
});
}}>
<dees-panel .title=${'7. Backward Compatibility'} .subtitle=${'Old button types are automatically mapped to new variants'}> <dees-panel .title=${'7. Backward Compatibility'} .subtitle=${'Old button types are automatically mapped to new variants'}>
<div class="button-group"> <div class="button-group">
<dees-button type="normal">Normal → Default</dees-button> <dees-button type="normal">Normal → Default</dees-button>
@ -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. These legacy type values are maintained for backward compatibility but we recommend using the new variant system.
</p> </p>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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`);
}
});
});
}
}}>
<dees-panel .title=${'8. Advanced Examples'} .subtitle=${'Complex button configurations and real-world use cases'}> <dees-panel .title=${'8. Advanced Examples'} .subtitle=${'Complex button configurations and real-world use cases'}>
<div class="horizontal-group"> <div class="horizontal-group">
<div class="vertical-group"> <div class="vertical-group">
@ -296,6 +416,6 @@ export const demoFunc = () => html`
</div> </div>
</div> </div>
</dees-panel> </dees-panel>
</div> </dees-demowrapper>
</dees-demowrapper> </div>
`; `;

View File

@ -3,40 +3,91 @@ import type { DeesForm } from './dees-form.js';
import '@design.estate/dees-wcctools/demotools'; import '@design.estate/dees-wcctools/demotools';
export const demoFunc = () => html` export const demoFunc = () => html`
<dees-demowrapper> <style>
<style> ${css`
${css` .demo-container {
.demo-container { display: flex;
display: flex; flex-direction: column;
flex-direction: column; gap: 24px;
gap: 24px; padding: 24px;
padding: 24px; max-width: 1200px;
max-width: 1200px; margin: 0 auto;
margin: 0 auto; }
}
dees-panel { dees-panel {
margin-bottom: 24px; margin-bottom: 24px;
} }
dees-panel:last-child { dees-panel:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
`}
</style>
<div class="demo-container"> .form-output {
margin-top: 16px;
padding: 12px;
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')};
border-radius: 6px;
font-size: 14px;
font-family: monospace;
white-space: pre-wrap;
}
.status-message {
margin-top: 16px;
padding: 12px;
border-radius: 6px;
font-size: 14px;
}
.status-message.success {
background: ${cssManager.bdTheme('hsl(142.1 70.6% 45.3% / 0.1)', 'hsl(142.1 70.6% 45.3% / 0.2)')};
color: ${cssManager.bdTheme('hsl(142.1 70.6% 35.3%)', 'hsl(142.1 70.6% 65.3%)')};
}
.status-message.error {
background: ${cssManager.bdTheme('hsl(0 72.2% 50.6% / 0.1)', 'hsl(0 72.2% 50.6% / 0.2)')};
color: ${cssManager.bdTheme('hsl(0 72.2% 40.6%)', 'hsl(0 72.2% 60.6%)')};
}
`}
</style>
<div class="demo-container">
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
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 = `<strong>Submitted Data:</strong>\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 = '<em>Form has been reset</em>';
});
// 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'));
});
});
}
}}>
<dees-panel .heading="Complete Form Example" .description="A comprehensive form with various input types, validation, and form submission handling"> <dees-panel .heading="Complete Form Example" .description="A comprehensive form with various input types, validation, and form submission handling">
<dees-form <dees-form>
@formData=${async (eventArg) => {
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();
}}
>
<dees-input-text <dees-input-text
.required=${true} .required=${true}
key="firstName" key="firstName"
@ -92,13 +143,47 @@ export const demoFunc = () => html`
<dees-form-submit>Create Account</dees-form-submit> <dees-form-submit>Create Account</dees-form-submit>
</dees-form> </dees-form>
</dees-panel>
<div class="form-output">
<em>Submit the form to see the collected data...</em>
</div>
</dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
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);
});
});
}
}}>
<dees-panel .heading="Horizontal Form Layout" .description="Compact form with inputs arranged horizontally - perfect for filters and quick forms"> <dees-panel .heading="Horizontal Form Layout" .description="Compact form with inputs arranged horizontally - perfect for filters and quick forms">
<dees-form horizontal-layout> <dees-form horizontal-layout>
<dees-input-text <dees-input-text
key="search" key="search"
label="Search" label="Search"
placeholder="Enter keywords..."
></dees-input-text> ></dees-input-text>
<dees-input-dropdown <dees-input-dropdown
@ -132,16 +217,55 @@ export const demoFunc = () => html`
></dees-input-checkbox> ></dees-input-checkbox>
</dees-form> </dees-form>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
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`);
});
}
}
}}>
<dees-panel .heading="Advanced Form Features" .description="Form with specialized input types and complex validation"> <dees-panel .heading="Advanced Form Features" .description="Form with specialized input types and complex validation">
<dees-form <dees-form>
@formData=${async (eventArg) => {
const form: DeesForm = eventArg.currentTarget;
const data = eventArg.detail.data;
console.log('Form data:', data);
form.setStatus('success', 'Data logged to console!');
}}
>
<dees-input-iban <dees-input-iban
key="iban" key="iban"
label="IBAN" label="IBAN"
@ -181,7 +305,9 @@ export const demoFunc = () => html`
<dees-form-submit>Submit Application</dees-form-submit> <dees-form-submit>Submit Application</dees-form-submit>
</dees-form> </dees-form>
<div id="status-display"></div>
</dees-panel> </dees-panel>
</div> </dees-demowrapper>
</dees-demowrapper> </div>
`; `;

View File

@ -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 './dees-input-datepicker.js';
import type { DeesInputDatepicker } from './dees-input-datepicker.js';
export const demoFunc = () => html` export const demoFunc = () => html`
<style> <style>
.demo-container { ${css`
display: flex; .demo-container {
flex-direction: column; display: flex;
gap: 32px; flex-direction: column;
padding: 32px; gap: 24px;
max-width: 600px; padding: 24px;
margin: 0 auto; max-width: 1200px;
} margin: 0 auto;
}
.demo-section { dees-panel {
display: flex; margin-bottom: 24px;
flex-direction: column; }
gap: 16px;
}
.demo-title { dees-panel:last-child {
font-size: 18px; margin-bottom: 0;
font-weight: 600; }
margin-bottom: 8px;
}
.demo-description { .demo-output {
font-size: 14px; margin-top: 16px;
color: #666; padding: 12px;
margin-bottom: 16px; background: rgba(0, 105, 242, 0.1);
} border-radius: 4px;
font-size: 14px;
font-family: monospace;
}
.date-group {
display: flex;
gap: 16px;
flex-wrap: wrap;
}
`}
</style> </style>
<div class="demo-container"> <div class="demo-container">
<div class="demo-section"> <dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
<div class="demo-title">Basic Date Picker</div> // Demonstrate basic date picker functionality
<div class="demo-description">Simple date selection without time</div> const datePicker = elementArg.querySelector('dees-input-datepicker');
<dees-input-datepicker
label="Select Date"
description="Choose a date from the calendar"
placeholder="Pick a date"
></dees-input-datepicker>
</div>
<div class="demo-section"> if (datePicker) {
<div class="demo-title">Date and Time Picker</div> datePicker.addEventListener('change', (event: CustomEvent) => {
<div class="demo-description">Date selection with time in 24-hour format</div> console.log('Basic date selected:', (event.target as DeesInputDatepicker).value);
<dees-input-datepicker });
label="Event Date & Time" }
description="Select both date and time" }}>
.enableTime=${true} <dees-panel .title=${'Basic Date Picker'} .subtitle=${'Simple date selection without time'}>
timeFormat="24h" <dees-input-datepicker
></dees-input-datepicker> label="Select Date"
</div> description="Choose a date from the calendar"
placeholder="Pick a date"
></dees-input-datepicker>
</dees-panel>
</dees-demowrapper>
<div class="demo-section"> <dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
<div class="demo-title">12-Hour Time Format</div> // Demonstrate date and time picker
<div class="demo-description">Date and time with AM/PM selector</div> const dateTimePicker = elementArg.querySelector('dees-input-datepicker[label="Event Date & Time"]');
<dees-input-datepicker const appointmentPicker = elementArg.querySelector('dees-input-datepicker[label="Appointment"]');
label="Appointment"
.enableTime=${true}
timeFormat="12h"
.minuteIncrement=${15}
></dees-input-datepicker>
</div>
<div class="demo-section"> if (dateTimePicker) {
<div class="demo-title">Date Range Constraints</div> dateTimePicker.addEventListener('change', (event: CustomEvent) => {
<div class="demo-description">Limit selectable dates with min and max</div> const value = (event.target as DeesInputDatepicker).value;
<dees-input-datepicker console.log('24h format datetime:', value);
label="Future Date Only" });
description="Can only select dates from today onwards" }
.minDate=${new Date().toISOString()}
.maxDate=${new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString()}
></dees-input-datepicker>
</div>
<div class="demo-section"> if (appointmentPicker) {
<div class="demo-title">Custom Date Format</div> appointmentPicker.addEventListener('change', (event: CustomEvent) => {
<div class="demo-description">Different date display format</div> const value = (event.target as DeesInputDatepicker).value;
<dees-input-datepicker console.log('12h format datetime:', value);
label="European Format" });
dateFormat="DD/MM/YYYY" }
.value=${new Date().toISOString()} }}>
></dees-input-datepicker> <dees-panel .title=${'Date and Time Selection'} .subtitle=${'Date pickers with time selection in different formats'}>
</div> <dees-input-datepicker
label="Event Date & Time"
description="Select both date and time (24-hour format)"
.enableTime=${true}
timeFormat="24h"
></dees-input-datepicker>
<div class="demo-section"> <dees-input-datepicker
<div class="demo-title">Required Field</div> label="Appointment"
<div class="demo-description">Date picker as a required form field</div> description="Date and time with AM/PM selector (15-minute increments)"
<dees-input-datepicker .enableTime=${true}
label="Birth Date" timeFormat="12h"
description="This field is required" .minuteIncrement=${15}
.required=${true} ></dees-input-datepicker>
placeholder="Select your birth date" </dees-panel>
></dees-input-datepicker> </dees-demowrapper>
</div>
<div class="demo-section"> <dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
<div class="demo-title">Disabled State</div> // Demonstrate date constraints
<div class="demo-description">Date picker in disabled state</div> const futureDatePicker = elementArg.querySelector('dees-input-datepicker');
<dees-input-datepicker
label="Disabled Date"
.disabled=${true}
.value=${new Date().toISOString()}
></dees-input-datepicker>
</div>
<div class="demo-section"> if (futureDatePicker) {
<div class="demo-title">Week Starts on Sunday</div> // Show the min/max constraints in action
<div class="demo-description">Calendar with Sunday as first day of week</div> futureDatePicker.addEventListener('change', (event: CustomEvent) => {
<dees-input-datepicker const value = (event.target as DeesInputDatepicker).value;
label="US Calendar" if (value) {
.weekStartsOn=${0} const selectedDate = new Date(value);
></dees-input-datepicker> const today = new Date();
</div> const daysDiff = Math.floor((selectedDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
console.log(`Selected date is ${daysDiff} days from today`);
}
});
}
}}>
<dees-panel .title=${'Date Range Constraints'} .subtitle=${'Limit selectable dates with min and max values'}>
<dees-input-datepicker
label="Future Date Only"
description="Can only select dates from today to 90 days in the future"
.minDate=${new Date().toISOString()}
.maxDate=${new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString()}
></dees-input-datepicker>
</dees-panel>
</dees-demowrapper>
<div class="demo-section"> <dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
<div class="demo-title">With Disabled Dates</div> // Demonstrate different date formats
<div class="demo-description">Some dates are disabled and cannot be selected</div> const formatters = {
<dees-input-datepicker 'DD/MM/YYYY': 'European',
label="Availability Calendar" 'MM/DD/YYYY': 'US',
description="Weekends are disabled" 'YYYY-MM-DD': 'ISO'
.disabledDates=${[ };
new Date(2024, 0, 6).toISOString(), // Saturday
new Date(2024, 0, 7).toISOString(), // Sunday
new Date(2024, 0, 13).toISOString(), // Saturday
new Date(2024, 0, 14).toISOString(), // Sunday
new Date(2024, 0, 20).toISOString(), // Saturday
new Date(2024, 0, 21).toISOString(), // Sunday
new Date(2024, 0, 27).toISOString(), // Saturday
new Date(2024, 0, 28).toISOString(), // Sunday
]}
></dees-input-datepicker>
</div>
<div class="demo-section"> const datePickers = elementArg.querySelectorAll('dees-input-datepicker');
<div class="demo-title">Event Listeners</div> datePickers.forEach((picker) => {
<div class="demo-description">Check console for change events</div> picker.addEventListener('change', (event: CustomEvent) => {
<dees-input-datepicker const target = event.target as DeesInputDatepicker;
label="Event Demo" // Log the formatted value that's displayed in the input
@change="${(e: CustomEvent) => console.log('Date changed:', (e.target as any).value)}" const input = target.shadowRoot?.querySelector('.date-input') as HTMLInputElement;
></dees-input-datepicker> if (input) {
</div> console.log(`${target.label} format:`, input.value);
}
});
});
}}>
<dees-panel .title=${'Date Formats'} .subtitle=${'Different date display formats for various regions'}>
<div class="date-group">
<dees-input-datepicker
label="European Format"
dateFormat="DD/MM/YYYY"
.value=${new Date().toISOString()}
></dees-input-datepicker>
<dees-input-datepicker
label="US Format"
dateFormat="MM/DD/YYYY"
.value=${new Date().toISOString()}
></dees-input-datepicker>
<dees-input-datepicker
label="ISO Format"
dateFormat="YYYY-MM-DD"
.value=${new Date().toISOString()}
></dees-input-datepicker>
</div>
</dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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');
}
});
}
}}>
<dees-panel .title=${'Form States'} .subtitle=${'Required and disabled states'}>
<dees-input-datepicker
label="Birth Date"
description="This field is required"
.required=${true}
placeholder="Select your birth date"
></dees-input-datepicker>
<dees-input-datepicker
label="Disabled Date"
description="This field cannot be edited"
.disabled=${true}
.value=${new Date().toISOString()}
></dees-input-datepicker>
</dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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)');
}
}}>
<dees-panel .title=${'Calendar Customization'} .subtitle=${'Different week start days for various regions'}>
<div class="date-group">
<dees-input-datepicker
label="US Calendar"
description="Week starts on Sunday"
.weekStartsOn=${0}
></dees-input-datepicker>
<dees-input-datepicker
label="EU Calendar"
description="Week starts on Monday"
.weekStartsOn=${1}
></dees-input-datepicker>
</div>
</dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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');
}
}}>
<dees-panel .title=${'Disabled Dates'} .subtitle=${'Calendar with specific dates disabled (weekends in current month)'}>
<dees-input-datepicker
label="Availability Calendar"
description="Weekends are disabled for the current month"
></dees-input-datepicker>
</dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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 = `
<strong>Event triggered!</strong><br>
ISO Value: ${value}<br>
Formatted: ${formattedValue}<br>
Date object: ${date.toLocaleString()}
`;
} else {
output.innerHTML = '<em>Date cleared</em>';
}
});
picker.addEventListener('blur', () => {
console.log('Datepicker lost focus');
});
}
}}>
<dees-panel .title=${'Event Handling'} .subtitle=${'Interactive demonstration of change events'}>
<dees-input-datepicker
label="Event Demo"
description="Select a date to see the event details"
></dees-input-datepicker>
<div id="event-output" class="demo-output">
<em>Select a date to see event details...</em>
</div>
</dees-panel>
</dees-demowrapper>
</div> </div>
`; `;

View File

@ -627,7 +627,7 @@ export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
} }
} }
private formatDate(isoString: string): string { public formatDate(isoString: string): string {
if (!isoString) return ''; if (!isoString) return '';
try { try {

View File

@ -5,45 +5,63 @@ import './dees-form.js';
import './dees-form-submit.js'; import './dees-form-submit.js';
export const demoFunc = () => html` export const demoFunc = () => html`
<dees-demowrapper> <style>
<style> ${css`
${css` .demo-container {
.demo-container { display: flex;
display: flex; flex-direction: column;
flex-direction: column; gap: 24px;
gap: 24px; padding: 24px;
padding: 24px; max-width: 1200px;
max-width: 1200px; margin: 0 auto;
margin: 0 auto; }
}
dees-panel { dees-panel {
margin-bottom: 24px; margin-bottom: 24px;
} }
dees-panel:last-child { dees-panel:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
.horizontal-group { .horizontal-group {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: 16px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.spacer { .spacer {
height: 200px; height: 200px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #999; color: #999;
font-size: 14px; font-size: 14px;
} }
`} `}
</style> </style>
<div class="demo-container"> <div class="demo-container">
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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);
});
}
}}>
<dees-panel .title=${'1. Basic Dropdowns'} .subtitle=${'Standard dropdown with search functionality and various options'}> <dees-panel .title=${'1. Basic Dropdowns'} .subtitle=${'Standard dropdown with search functionality and various options'}>
<dees-input-dropdown <dees-input-dropdown
.label=${'Select Country'} .label=${'Select Country'}
@ -70,7 +88,18 @@ export const demoFunc = () => html`
]} ]}
></dees-input-dropdown> ></dees-input-dropdown>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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}`);
});
}
}}>
<dees-panel .title=${'2. Without Search'} .subtitle=${'Dropdown with search functionality disabled for simpler selection'}> <dees-panel .title=${'2. Without Search'} .subtitle=${'Dropdown with search functionality disabled for simpler selection'}>
<dees-input-dropdown <dees-input-dropdown
.label=${'Priority Level'} .label=${'Priority Level'}
@ -83,7 +112,20 @@ export const demoFunc = () => html`
.selectedOption=${{ option: 'Medium', key: 'medium' }} .selectedOption=${{ option: 'Medium', key: 'medium' }}
></dees-input-dropdown> ></dees-input-dropdown>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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}`);
});
});
}}>
<dees-panel .title=${'3. Horizontal Layout'} .subtitle=${'Multiple dropdowns in a horizontal layout for compact forms'}> <dees-panel .title=${'3. Horizontal Layout'} .subtitle=${'Multiple dropdowns in a horizontal layout for compact forms'}>
<div class="horizontal-group"> <div class="horizontal-group">
<dees-input-dropdown <dees-input-dropdown
@ -120,7 +162,19 @@ export const demoFunc = () => html`
></dees-input-dropdown> ></dees-input-dropdown>
</div> </div>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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');
});
}
}}>
<dees-panel .title=${'4. States'} .subtitle=${'Different states and configurations'}> <dees-panel .title=${'4. States'} .subtitle=${'Different states and configurations'}>
<dees-input-dropdown <dees-input-dropdown
.label=${'Required Field'} .label=${'Required Field'}
@ -141,11 +195,25 @@ export const demoFunc = () => html`
.selectedOption=${{ option: 'Cannot Select', key: 'disabled' }} .selectedOption=${{ option: 'Cannot Select', key: 'disabled' }}
></dees-input-dropdown> ></dees-input-dropdown>
</dees-panel> </dees-panel>
</dees-demowrapper>
<div class="spacer"> <div class="spacer">
(Spacer to test dropdown positioning) (Spacer to test dropdown positioning)
</div> </div>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// This dropdown demonstrates automatic positioning
const dropdown = elementArg.querySelector('dees-input-dropdown');
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
}
}}>
<dees-panel .title=${'5. Bottom Positioning'} .subtitle=${'Dropdown that opens upward when near bottom of viewport'}> <dees-panel .title=${'5. Bottom Positioning'} .subtitle=${'Dropdown that opens upward when near bottom of viewport'}>
<dees-input-dropdown <dees-input-dropdown
.label=${'Opens Upward'} .label=${'Opens Upward'}
@ -158,7 +226,30 @@ export const demoFunc = () => html`
]} ]}
></dees-input-dropdown> ></dees-input-dropdown>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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 = '<em>Select a product to see details...</em>';
// Handle dropdown changes
dropdown.addEventListener('change', (event: CustomEvent) => {
if (event.detail.value) {
output.innerHTML = `
<strong>Selected:</strong> ${event.detail.value.option}<br>
<strong>Key:</strong> ${event.detail.value.key}<br>
<strong>Price:</strong> $${event.detail.value.payload?.price || 'N/A'}<br>
<strong>Features:</strong> ${event.detail.value.payload?.features?.join(', ') || 'N/A'}
`;
}
});
}
}}>
<dees-panel .title=${'6. Event Handling & Payload'} .subtitle=${'Dropdown with payload data and change event handling'}> <dees-panel .title=${'6. Event Handling & Payload'} .subtitle=${'Dropdown with payload data and change event handling'}>
<dees-input-dropdown <dees-input-dropdown
.label=${'Select Product'} .label=${'Select Product'}
@ -167,24 +258,35 @@ export const demoFunc = () => html`
{ option: 'Pro Plan', key: 'pro', payload: { price: 19.99, features: ['Feature A', 'Feature B'] } }, { 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'] } } { 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 = `
<strong>Selected:</strong> ${e.detail.value.option}<br>
<strong>Key:</strong> ${e.detail.value.key}<br>
<strong>Price:</strong> $${e.detail.value.payload?.price || 'N/A'}<br>
<strong>Features:</strong> ${e.detail.value.payload?.features?.join(', ') || 'N/A'}
`;
}
}}
></dees-input-dropdown> ></dees-input-dropdown>
<div id="selection-output" style="margin-top: 16px; padding: 12px; background: rgba(0, 105, 242, 0.1); border-radius: 4px; font-size: 14px;"> <div id="selection-output" style="margin-top: 16px; padding: 12px; background: rgba(0, 105, 242, 0.1); border-radius: 4px; font-size: 14px;"></div>
<em>Select a product to see details...</em>
</div>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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
});
}
}}>
<dees-panel .title=${'7. Form Integration'} .subtitle=${'Dropdown working within a form with validation'}> <dees-panel .title=${'7. Form Integration'} .subtitle=${'Dropdown working within a form with validation'}>
<dees-form> <dees-form>
<dees-input-dropdown <dees-input-dropdown
@ -216,6 +318,6 @@ export const demoFunc = () => html`
<dees-form-submit .text=${'Create Project'}></dees-form-submit> <dees-form-submit .text=${'Create Project'}></dees-form-submit>
</dees-form> </dees-form>
</dees-panel> </dees-panel>
</div> </dees-demowrapper>
</dees-demowrapper> </div>
` `

View File

@ -1,67 +1,87 @@
import { html, css, cssManager } from '@design.estate/dees-element'; import { html, css, cssManager } from '@design.estate/dees-element';
import '@design.estate/dees-wcctools/demotools'; import '@design.estate/dees-wcctools/demotools';
import './dees-panel.js'; import './dees-panel.js';
import type { DeesInputText } from './dees-input-text.js';
export const demoFunc = () => html` export const demoFunc = () => html`
<dees-demowrapper> <style>
<style> ${css`
${css` .demo-container {
.demo-container { display: flex;
display: flex; flex-direction: column;
flex-direction: column; gap: 24px;
gap: 24px; padding: 24px;
padding: 24px; max-width: 1200px;
max-width: 1200px; margin: 0 auto;
margin: 0 auto; }
}
dees-panel { dees-panel {
margin-bottom: 24px; margin-bottom: 24px;
} }
dees-panel:last-child { dees-panel:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
.horizontal-group { .horizontal-group {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: 16px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.grid-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
@media (max-width: 768px) {
.grid-layout { .grid-layout {
display: grid; grid-template-columns: 1fr;
grid-template-columns: 1fr 1fr;
gap: 16px;
} }
}
@media (max-width: 768px) { .interactive-section {
.grid-layout { background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')};
grid-template-columns: 1fr; border-radius: 8px;
} padding: 16px;
} margin-top: 16px;
}
.interactive-section { .output-text {
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')}; font-family: monospace;
border-radius: 8px; font-size: 13px;
padding: 16px; color: ${cssManager.bdTheme('hsl(215.3 25% 26.7%)', 'hsl(210 40% 80%)')};
margin-top: 16px; padding: 8px;
} background: ${cssManager.bdTheme('hsl(210 40% 98%)', 'hsl(215 20.2% 11.8%)')};
border-radius: 4px;
min-height: 24px;
}
`}
</style>
.output-text { <div class="demo-container">
font-family: monospace; <dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
font-size: 13px; // Demonstrate basic text input functionality
color: ${cssManager.bdTheme('hsl(215.3 25% 26.7%)', 'hsl(210 40% 80%)')}; const inputs = elementArg.querySelectorAll('dees-input-text');
padding: 8px;
background: ${cssManager.bdTheme('hsl(210 40% 98%)', 'hsl(215 20.2% 11.8%)')};
border-radius: 4px;
min-height: 24px;
}
`}
</style>
<div class="demo-container"> inputs.forEach((input: DeesInputText) => {
input.addEventListener('changeSubject', (event: CustomEvent) => {
console.log(`Input "${input.label}" changed to:`, input.getValue());
});
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');
}
}}>
<dees-panel .title=${'Basic Text Inputs'} .subtitle=${'Standard text inputs with labels and descriptions'}> <dees-panel .title=${'Basic Text Inputs'} .subtitle=${'Standard text inputs with labels and descriptions'}>
<dees-input-text <dees-input-text
.label=${'Username'} .label=${'Username'}
@ -83,7 +103,33 @@ export const demoFunc = () => html`
.key=${'password'} .key=${'password'}
></dees-input-text> ></dees-input-text>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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);
}
}}>
<dees-panel .title=${'Horizontal Layout'} .subtitle=${'Multiple inputs arranged horizontally for compact forms'}> <dees-panel .title=${'Horizontal Layout'} .subtitle=${'Multiple inputs arranged horizontally for compact forms'}>
<div class="horizontal-group"> <div class="horizontal-group">
<dees-input-text <dees-input-text
@ -108,7 +154,23 @@ export const demoFunc = () => html`
></dees-input-text> ></dees-input-text>
</div> </div>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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`);
}
}}>
<dees-panel .title=${'Label Positions'} .subtitle=${'Different label positioning options for various layouts'}> <dees-panel .title=${'Label Positions'} .subtitle=${'Different label positioning options for various layouts'}>
<dees-input-text <dees-input-text
.label=${'Label on Top (Default)'} .label=${'Label on Top (Default)'}
@ -136,7 +198,41 @@ export const demoFunc = () => html`
></dees-input-text> ></dees-input-text>
</div> </div>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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!');
}
});
}
}}>
<dees-panel .title=${'Validation & States'} .subtitle=${'Different validation states and input configurations'}> <dees-panel .title=${'Validation & States'} .subtitle=${'Different validation states and input configurations'}>
<dees-input-text <dees-input-text
.label=${'Required Field'} .label=${'Required Field'}
@ -157,7 +253,31 @@ export const demoFunc = () => html`
.validationState=${'invalid'} .validationState=${'invalid'}
></dees-input-text> ></dees-input-text>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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'] });
}
}
});
}}>
<dees-panel .title=${'Advanced Features'} .subtitle=${'Password visibility toggle and other advanced features'}> <dees-panel .title=${'Advanced Features'} .subtitle=${'Password visibility toggle and other advanced features'}>
<dees-input-text <dees-input-text
.label=${'Password with Toggle'} .label=${'Password with Toggle'}
@ -173,23 +293,47 @@ export const demoFunc = () => html`
.description=${'Keep this key secure and never share it'} .description=${'Keep this key secure and never share it'}
></dees-input-text> ></dees-input-text>
</dees-panel> </dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// 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}`);
});
}
}}>
<dees-panel .title=${'Interactive Example'} .subtitle=${'Try typing in the inputs to see real-time value changes'}> <dees-panel .title=${'Interactive Example'} .subtitle=${'Try typing in the inputs to see real-time value changes'}>
<dees-input-text <dees-input-text
.label=${'Dynamic Input'} .label=${'Dynamic Input'}
.placeholder=${'Type something here...'} .placeholder=${'Type something here...'}
@changeSubject=${(event) => {
const output = document.querySelector('#text-input-output');
if (output && event.detail) {
output.textContent = `Current value: "${event.detail.getValue()}"`;
}
}}
></dees-input-text> ></dees-input-text>
<div class="interactive-section"> <div class="interactive-section">
<div id="text-input-output" class="output-text">Current value: ""</div> <div id="text-input-output" class="output-text">Current value: ""</div>
</div> </div>
</dees-panel> </dees-panel>
</div> </dees-demowrapper>
</dees-demowrapper> </div>
`; `;