Compare commits

...

14 Commits

Author SHA1 Message Date
22e6b74c4f 1.9.0
Some checks failed
Default (tags) / security (push) Failing after 27s
Default (tags) / test (push) Failing after 12s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-22 20:32:59 +00:00
4de835474b feat(form-inputs): Improve form input consistency and auto spacing across inputs and buttons 2025-06-22 20:32:59 +00:00
024d8af40d 1.8.20
Some checks failed
Default (tags) / security (push) Failing after 15s
Default (tags) / test (push) Failing after 21s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-20 00:11:30 +00:00
808b74fa17 fix(deps): Update dependency versions: bump @design.estate/dees-domtools from ^2.1.1 to ^2.3.3, @design.estate/dees-element from ^2.0.42 to ^2.0.44, lucide from ^0.515.0 to ^0.518.0, and @git.zone/tsbundle from ^2.0.15 to ^2.4.0 2025-06-20 00:11:30 +00:00
202881ef1a 1.8.19
Some checks failed
Default (tags) / security (push) Failing after 42s
Default (tags) / test (push) Failing after 35s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-19 14:01:14 +00:00
7de3d451ad fix: Change import to type for DeesForm in dees-form-submit 2025-06-19 14:01:07 +00:00
f0e0430016 1.8.18
Some checks failed
Default (tags) / security (push) Failing after 44s
Default (tags) / test (push) Failing after 35s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-19 13:48:18 +00:00
873579fc97 fix: Import dees-button in dees-form-submit for button functionality 2025-06-19 13:47:52 +00:00
d321db363d 1.8.17
Some checks failed
Default (tags) / security (push) Failing after 45s
Default (tags) / test (push) Failing after 37s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-19 12:54:51 +00:00
73c1874e3f 1.8.16
Some checks failed
Default (tags) / security (push) Failing after 45s
Default (tags) / test (push) Failing after 38s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-19 12:53:28 +00:00
1aa06398a0 1.8.15
Some checks failed
Default (tags) / security (push) Failing after 47s
Default (tags) / test (push) Failing after 38s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-19 12:42:53 +00:00
99b23236a1 fix: Update default button text handling and improve demo example in dees-form-submit 2025-06-19 12:42:50 +00:00
d1e7e5447c 1.8.14
Some checks failed
Default (tags) / security (push) Failing after 49s
Default (tags) / test (push) Failing after 38s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-19 12:31:45 +00:00
4f22a98b78 refactor: Remove unnecessary imports in dees-form-submit and dees-simple-login 2025-06-19 12:31:33 +00:00
16 changed files with 1159 additions and 809 deletions

View File

@ -1,5 +1,22 @@
# Changelog # Changelog
## 2025-06-22 - 1.9.0 - feat(form-inputs)
Improve form input consistency and auto spacing across inputs and buttons
- Add an 'insideForm' property to dees-button for auto-detection and proper margin adjustment in forms.
- Update dees-input-radio to include a 'name' property so that radio buttons in the same group are mutually exclusive.
- Enhance dees-form to group radio inputs properly when collecting form data.
- Revise readme.hints.md and readme.plan.md to document changes and provide guidance for dees-input-radio.
- Update demos for dees-button and dees-form to showcase correct spacing in vertical and horizontal layouts.
## 2025-06-20 - 1.8.20 - fix(deps)
Update dependency versions: bump @design.estate/dees-domtools from ^2.1.1 to ^2.3.3, @design.estate/dees-element from ^2.0.42 to ^2.0.44, lucide from ^0.515.0 to ^0.518.0, and @git.zone/tsbundle from ^2.0.15 to ^2.4.0
- Upgrade @design.estate/dees-domtools from ^2.1.1 to ^2.3.3
- Upgrade @design.estate/dees-element from ^2.0.42 to ^2.0.44
- Upgrade lucide from ^0.515.0 to ^0.518.0
- Upgrade @git.zone/tsbundle from ^2.0.15 to ^2.4.0
## 2025-06-10 - 1.8.1 - fix(dees-statsgrid) ## 2025-06-10 - 1.8.1 - fix(dees-statsgrid)
Adjust stats grid styling for better alignment and improved visualizations in gauge and trend tiles. Adjust stats grid styling for better alignment and improved visualizations in gauge and trend tiles.

View File

@ -1,6 +1,6 @@
{ {
"name": "@design.estate/dees-catalog", "name": "@design.estate/dees-catalog",
"version": "1.8.13", "version": "1.9.0",
"private": false, "private": false,
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.", "description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
"main": "dist_ts_web/index.js", "main": "dist_ts_web/index.js",
@ -15,8 +15,8 @@
"author": "Lossless GmbH", "author": "Lossless GmbH",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@design.estate/dees-domtools": "^2.1.1", "@design.estate/dees-domtools": "^2.3.3",
"@design.estate/dees-element": "^2.0.42", "@design.estate/dees-element": "^2.0.44",
"@design.estate/dees-wcctools": "^1.0.98", "@design.estate/dees-wcctools": "^1.0.98",
"@fortawesome/fontawesome-svg-core": "^6.7.2", "@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2", "@fortawesome/free-brands-svg-icons": "^6.7.2",
@ -30,7 +30,7 @@
"apexcharts": "^4.7.0", "apexcharts": "^4.7.0",
"highlight.js": "11.11.1", "highlight.js": "11.11.1",
"ibantools": "^4.5.1", "ibantools": "^4.5.1",
"lucide": "^0.515.0", "lucide": "^0.518.0",
"monaco-editor": "^0.52.2", "monaco-editor": "^0.52.2",
"pdfjs-dist": "^4.10.38", "pdfjs-dist": "^4.10.38",
"xterm": "^5.3.0", "xterm": "^5.3.0",
@ -38,7 +38,7 @@
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^2.6.4", "@git.zone/tsbuild": "^2.6.4",
"@git.zone/tsbundle": "^2.0.15", "@git.zone/tsbundle": "^2.4.0",
"@git.zone/tstest": "^2.3.1", "@git.zone/tstest": "^2.3.1",
"@git.zone/tswatch": "^2.0.37", "@git.zone/tswatch": "^2.0.37",
"@push.rocks/projectinfo": "^5.0.2", "@push.rocks/projectinfo": "^5.0.2",

860
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -58,4 +58,22 @@
- View mode selectors - View mode selectors
- Action grouping - Action grouping
- Navigation options - Navigation options
- Filter controls - Filter controls
## Form Components
### dees-input-radio
- Radio button component with proper group behavior
- Properties:
- `name`: Group name for mutually exclusive selection
- `key`: Unique identifier for the radio option
- `value`: Boolean indicating selection state
- `label`: Display label
- Features:
- Automatic group management (radios with same name are mutually exclusive)
- Cannot be deselected by clicking (proper radio behavior)
- Form integration: Radio groups are collected by name, value is the selected radio's key
- Works both inside and outside forms
- Supports disabled state
- Fixed: Radio buttons now properly deselect others in the group on first click
- Note: When using in forms, set both `name` (for grouping) and `key` (for the value)

View File

@ -1,213 +1,159 @@
# Input Component Unification Plan # Command: cat ~/.claude/CLAUDE.md
Command to reread guidelines: `cat /home/philkunz/.claude/CLAUDE.md` # Margin Harmonization Plan for @design.estate/dees-catalog
## Problem Summary ## Implementation Status: Phase 1 Complete ✅
The dees-input components have inconsistent margin behavior causing vertical alignment issues in horizontal flexbox layouts: Phase 1 has been successfully implemented. Buttons now auto-detect form context and apply appropriate spacing.
- **dees-input-text**: 8px top, 24px bottom margin ## Objective
- **dees-input-dropdown**: 0px top, 24px bottom margin Implement consistent spacing across all form elements using auto-detection and CSS-based approach for buttons while maintaining the existing input spacing system.
- **dees-input-checkbox/radio**: 20px top, 20px bottom margin
- Different components use different label implementations (some use dees-label, others have built-in labels)
## Proposed Solution ## Current Issues
- Buttons have no default margins (inconsistent with inputs)
- Manual spacing required when mixing buttons with inputs in forms
- No unified spacing constants across components
### 1. Standardize Margin System ## Implementation Plan (Improved)
Create a unified margin approach for all input components: ### Phase 1: Add Auto-Detected Form Spacing to Buttons ✅
- [x] Update `dees-button.ts`
- Add `insideForm` property (boolean, reflected) with auto-detection
- Add connectedCallback for automatic form detection:
```typescript
@property({ type: Boolean, reflect: true })
public insideForm: boolean = false;
connectedCallback() {
super.connectedCallback();
// Auto-detect if inside a form
if (!this.insideForm && this.closest('dees-form')) {
this.insideForm = true;
}
}
```
- Add margin styles for both vertical and horizontal form contexts:
```css
/* Default vertical form layout */
:host([inside-form]) {
display: block;
margin-bottom: var(--dees-input-vertical-gap);
}
:host([inside-form]:last-child) {
margin-bottom: 0;
}
/* Horizontal form layout - auto-detected via parent */
:host([inside-form]):host-context(dees-form[horizontal-layout]) {
display: inline-block;
margin-right: var(--dees-input-horizontal-gap);
margin-bottom: 0;
}
:host([inside-form]):host-context(dees-form[horizontal-layout]):last-child {
margin-right: 0;
}
```
- [x] Update `dees-form-submit.ts`
- Remove need for manual attribute setting (auto-detection handles it)
- Verify integration works correctly
```css ### Phase 2: Create Unified Spacing Constants (Optional Enhancement)
/* Default vertical stacking mode (for forms) */ - [ ] Add CSS custom properties to `dees-input-base.ts`:
:host { ```css
margin: 0; :root {
margin-bottom: 16px; /* Reduced from 24px for better density */ --dees-form-gap: 16px;
}
```
- [ ] Update existing `--dees-input-vertical-gap` to use `--dees-form-gap`
- [ ] Update button margins to use the same variable
### Phase 3: Testing and Documentation
- [ ] Test mixed forms with inputs and buttons (both vertical and horizontal)
- [ ] Verify last-child margin removal works
- [ ] Test auto-detection behavior
- [ ] Update demos:
- `dees-form.demo.ts` - show buttons auto-detecting form context
- `dees-button.demo.ts` - add form context example with manual override
- [ ] Document the `insideForm` property and auto-detection in readme.md
### Phase 4: Clean Up
- [ ] Ensure all spacing uses consistent values (16px)
- [ ] Verify no breaking changes
- [ ] Update changelog.md
## Technical Details
### Button Form Integration
```typescript
// In dees-button.ts
@property({ type: Boolean, reflect: true })
public insideForm: boolean = false;
connectedCallback() {
super.connectedCallback();
// Auto-detect if inside a form
if (!this.insideForm && this.closest('dees-form')) {
this.insideForm = true;
}
} }
/* Last child in container should have no bottom margin */ // CSS addition
:host(:last-child) { /* Default vertical form layout */
:host([inside-form]) {
display: block;
margin-bottom: var(--dees-input-vertical-gap);
}
:host([inside-form]:last-child) {
margin-bottom: 0; margin-bottom: 0;
} }
/* Horizontal layout mode - activated by parent context or attribute */ /* Horizontal form layout - auto-detected via parent */
:host([horizontal-layout]) { :host([inside-form]):host-context(dees-form[horizontal-layout]) {
margin: 0; display: inline-block;
margin-right: 16px; margin-right: var(--dees-input-horizontal-gap);
margin-bottom: 0; margin-bottom: 0;
} }
:host([inside-form]):host-context(dees-form[horizontal-layout]):last-child {
:host([horizontal-layout]:last-child) {
margin-right: 0; margin-right: 0;
} }
``` ```
### 2. Unified Label Architecture ### Usage Example
```html
<!-- Automatic detection - buttons get form spacing automatically -->
<dees-form>
<dees-input-text label="Name"></dees-input-text>
<dees-input-email label="Email"></dees-input-email>
<dees-button>Save Draft</dees-button> <!-- Auto-detects form context -->
<dees-form-submit>Submit</dees-form-submit> <!-- Auto-detects form context -->
</dees-form>
All input components should use the `dees-label` component for consistency: <!-- Manual override if needed -->
<div class="custom-container">
<dees-button inside-form="true">Standalone button with form spacing</dees-button>
</div>
- Move label rendering from built-in implementations to `dees-label` usage <!-- Horizontal form - spacing adjusts automatically -->
- Add a `labelPosition` property to all inputs: `'top' | 'left' | 'right' | 'none'` <dees-form horizontal-layout>
- Default to 'top' for text/dropdown, 'right' for checkbox/radio <dees-input-text label="Search"></dees-input-text>
<dees-button>Search</dees-button> <!-- Gets right margin instead of bottom -->
### 3. Layout Mode Support </dees-form>
Add a `layoutMode` property to all input components:
```typescript
@property({ type: String })
public layoutMode: 'vertical' | 'horizontal' | 'auto' = 'auto';
``` ```
- `vertical`: Traditional form layout (label on top) ## Success Criteria
- `horizontal`: Inline layout (label position configurable) 1. Buttons automatically detect form context and apply appropriate spacing
- `auto`: Detect from parent context 2. Manual override available via `insideForm` property
3. Supports both vertical and horizontal form layouts
4. No breaking changes for existing implementations
5. Consistent 16px spacing between all form elements
6. Clear documentation and examples
7. All tests pass
### 4. Implementation Steps ## Benefits of This Approach
- **Automatic behavior** - Works out of the box, no manual attributes needed
1. **Create base input class** (`DeesInputBase`): - **Consistent with inputs** - Follows the same pattern as existing form elements
- Common margin styles - **Layout aware** - Automatically adapts to vertical/horizontal forms
- Layout mode detection - **Minimal code** - Simple CSS-based solution with light JS detection
- Label position handling - **Backward compatible** - Existing code continues to work
- Shared properties (key, required, disabled, value) - **Override capability** - Manual control when needed
- **Uses existing variables** - Leverages `--dees-input-vertical-gap` and `--dees-input-horizontal-gap`
2. **Update dees-input-text**:
- Extend from DeesInputBase
- Remove hardcoded margins
- Keep using dees-label component
3. **Update dees-input-dropdown**:
- Extend from DeesInputBase
- Remove hardcoded margins
- Switch from built-in label to dees-label
4. **Update dees-input-checkbox**:
- Extend from DeesInputBase
- Remove hardcoded margins
- Add support for label position (keep default as 'right')
- Switch to dees-label component
5. **Update dees-input-radio**:
- Same as checkbox
6. **Update dees-form**:
- Add property to control child input layout mode
- Ensure proper spacing context
### 5. CSS Variable System
Introduce CSS variables for consistent spacing:
```css
:host {
--dees-input-spacing-unit: 8px;
--dees-input-vertical-gap: calc(var(--dees-input-spacing-unit) * 2); /* 16px */
--dees-input-horizontal-gap: calc(var(--dees-input-spacing-unit) * 2); /* 16px */
--dees-input-label-gap: var(--dees-input-spacing-unit); /* 8px */
}
```
### 6. Backward Compatibility
- Keep existing properties and methods
- Add deprecation notices for properties that will be removed
- Provide migration guide in documentation
### 7. Testing Requirements
- Test all inputs in vertical form layouts
- Test all inputs in horizontal flexbox containers
- Test mixed input types in same container
- Test with and without labels
- Test theme switching (light/dark)
- Test responsive behavior
## Expected Outcome
- All input components will align properly in horizontal layouts
- Consistent spacing in vertical forms
- Unified label handling across all inputs
- Better developer experience with predictable behavior
- Maintained backward compatibility
## Timeline
1. Phase 1: Create DeesInputBase class and update dees-input-text ✅
2. Phase 2: Update remaining input components ✅
3. Phase 3: Update documentation and examples ✅
4. Phase 4: Testing and refinement ✅
## Implementation Status
### Completed:
1. **Created DeesInputBase class** (`dees-input-base.ts`):
- Generic base class with unified margin system
- Layout mode support (vertical/horizontal/auto)
- Label position control
- Common properties and methods
- CSS variables for consistent spacing
2. **Updated all input components**:
- `dees-input-text`: Now extends DeesInputBase, margins removed
- `dees-input-dropdown`: Now extends DeesInputBase, uses dees-label
- `dees-input-checkbox`: Now extends DeesInputBase, uses dees-label (default label position: right)
- `dees-input-radio`: Now extends DeesInputBase, uses dees-label (default label position: right)
- `dees-input-phone`: Now extends DeesInputBase with phone formatting functionality
- `dees-input-iban`: Now extends DeesInputBase with IBAN validation
- `dees-input-quantityselector`: Now extends DeesInputBase
- `dees-input-multitoggle`: Now extends DeesInputBase with value property for forms
- `dees-input-typelist`: Now extends DeesInputBase
- `dees-input-fileupload`: Now extends DeesInputBase, uses dees-label
3. **Updated dees-form**:
- Added `horizontal-layout` property
- Auto-detection of layout mode for child inputs
- Added dropdown to form input types
4. **Fixed TypeScript errors**:
- Added value property to dropdown for form compatibility
- Fixed changeSubject typing
- Updated form value type to include dropdown options
- Fixed firstUpdated method signatures (phone, iban, fileupload)
- Fixed CSS-in-JS errors in quantityselector (removed dynamic references)
- Added value property to multitoggle for form compatibility
- Removed duplicate properties in fileupload (label, key, disabled, required)
### Result:
All input components now have:
- Unified 16px bottom margin in vertical layouts
- 16px right margin in horizontal layouts
- No margin on last child
- Consistent label handling via dees-label
- Flexible layout modes
- Better alignment in flexbox containers
## Demo Improvements
### Created external demo files:
1. **dees-input-text.demo.ts**: Comprehensive demos showing:
- Basic text inputs with descriptions
- Horizontal layout examples
- Label position variations
- Validation states
- Password input features
2. **dees-input-checkbox.demo.ts**: Interactive demos featuring:
- Basic checkbox usage
- Horizontal layout groups
- Feature selection with batch operations
- Real-world examples
3. **dees-input-radio.demo.ts**: Radio button demos including:
- Radio groups with proper behavior
- Horizontal yes/no questions
- Survey-style layouts
- Settings examples
### Updated existing demos:
1. **dees-input-dropdown.demo.ts**: Enhanced with dees-demowrapper and comprehensive examples
2. **dees-form.demo.ts**: Added horizontal form layout examples and advanced form features
3. **dees-simple-appdash.demo.ts**: Enhanced settings view with horizontal forms and radio groups
All demos now use the `dees-demowrapper` component for consistency and include proper styling for light/dark themes.

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@design.estate/dees-catalog', name: '@design.estate/dees-catalog',
version: '1.8.1', version: '1.9.0',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.' description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
} }

View File

@ -1,6 +1,42 @@
import { html } from '@design.estate/dees-element'; import { html, css } from '@design.estate/dees-element';
export const demoFunc = () => html` export const demoFunc = () => html`
<style>
${css`
h3 {
margin-top: 32px;
margin-bottom: 16px;
color: #333;
}
@media (prefers-color-scheme: dark) {
h3 {
color: #ccc;
}
}
.form-demo {
background: #f5f5f5;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
@media (prefers-color-scheme: dark) {
.form-demo {
background: #1a1a1a;
}
}
.button-group {
display: flex;
gap: 16px;
margin: 20px 0;
}
`}
</style>
<h3>Button Types</h3>
<dees-button>This is a slotted Text</dees-button> <dees-button>This is a slotted Text</dees-button>
<p> <p>
<dees-button text="Highlighted: This text shows" type="highlighted">Highlighted</dees-button> <dees-button text="Highlighted: This text shows" type="highlighted">Highlighted</dees-button>
@ -8,8 +44,34 @@ export const demoFunc = () => html`
<p><dees-button type="discreet">This is discreete button</dees-button></p> <p><dees-button type="discreet">This is discreete button</dees-button></p>
<p><dees-button disabled>This is a disabled button</dees-button></p> <p><dees-button disabled>This is a disabled button</dees-button></p>
<p><dees-button type="big">This is a slotted Text</dees-button></p> <p><dees-button type="big">This is a slotted Text</dees-button></p>
<h3>Button States</h3>
<p><dees-button status="normal">Normal Status</dees-button></p> <p><dees-button status="normal">Normal Status</dees-button></p>
<p><dees-button disabled status="pending">Pending Status</dees-button></p> <p><dees-button disabled status="pending">Pending Status</dees-button></p>
<p><dees-button disabled status="success">Success Status</dees-button></p> <p><dees-button disabled status="success">Success Status</dees-button></p>
<p><dees-button disabled status="error">Error Status</dees-button></p> <p><dees-button disabled status="error">Error Status</dees-button></p>
<h3>Buttons in Forms (Auto-spacing)</h3>
<div class="form-demo">
<dees-form>
<dees-input-text label="Name" key="name"></dees-input-text>
<dees-input-text label="Email" key="email"></dees-input-text>
<dees-button>Save Draft</dees-button>
<dees-button type="highlighted">Save and Continue</dees-button>
<dees-form-submit>Submit Form</dees-form-submit>
</dees-form>
</div>
<h3>Buttons Outside Forms (No auto-spacing)</h3>
<div class="button-group">
<dees-button>Button 1</dees-button>
<dees-button>Button 2</dees-button>
<dees-button>Button 3</dees-button>
</div>
<h3>Manual Form Spacing</h3>
<div>
<dees-button inside-form="true">Manually spaced button 1</dees-button>
<dees-button inside-form="true">Manually spaced button 2</dees-button>
</div>
`; `;

View File

@ -55,10 +55,24 @@ export class DeesButton extends DeesElement {
}) })
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal'; public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
@property({
type: Boolean,
reflect: true
})
public insideForm: boolean = false;
constructor() { constructor() {
super(); super();
} }
public async connectedCallback() {
await super.connectedCallback();
// Auto-detect if inside a form
if (!this.insideForm && this.closest('dees-form')) {
this.insideForm = true;
}
}
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
css` css`
@ -71,6 +85,27 @@ export class DeesButton extends DeesElement {
display: none; display: none;
} }
/* Form spacing styles */
/* Default vertical form layout */
:host([inside-form]) {
margin-bottom: 16px; /* Using standard 16px like inputs */
}
:host([inside-form]:last-child) {
margin-bottom: 0;
}
/* Horizontal form layout - auto-detected via parent */
dees-form[horizontal-layout] :host([inside-form]) {
display: inline-block;
margin-right: 16px;
margin-bottom: 0;
}
dees-form[horizontal-layout] :host([inside-form]:last-child) {
margin-right: 0;
}
.button { .button {
transition: all 0.1s , color 0s; transition: all 0.1s , color 0s;
position: relative; position: relative;
@ -181,7 +216,7 @@ export class DeesButton extends DeesElement {
${this.status === 'normal' ? html``: html` ${this.status === 'normal' ? html``: html`
<dees-spinner .bnw=${true} status="${this.status}"></dees-spinner> <dees-spinner .bnw=${true} status="${this.status}"></dees-spinner>
`} `}
<div class="textbox">${this.text ? this.text : this.textContent}</div> <div class="textbox">${this.text || html`<slot>Button</slot>`}</div>
</div> </div>
`; `;
} }
@ -202,9 +237,6 @@ export class DeesButton extends DeesElement {
} }
public async firstUpdated() { public async firstUpdated() {
if (!this.textContent) { // Don't set default text here as it interferes with slotted content
this.textContent = 'Button';
this.performUpdate();
}
} }
} }

View File

@ -0,0 +1,3 @@
import { html } from '@design.estate/dees-element';
export const demoFunc = () => html`<dees-form-submit>Submit Form</dees-form-submit>`;

View File

@ -1,3 +1,4 @@
import { demoFunc } from './dees-form-submit.demo.js';
import { import {
customElement, customElement,
html, html,
@ -5,10 +6,8 @@ import {
css, css,
cssManager, cssManager,
property, property,
type CSSResult,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import { DeesForm } from './dees-form.js'; import type { DeesForm } from './dees-form.js';
import './dees-button.js'; // Import to ensure dees-button is registered
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
@ -18,7 +17,7 @@ declare global {
@customElement('dees-form-submit') @customElement('dees-form-submit')
export class DeesFormSubmit extends DeesElement { export class DeesFormSubmit extends DeesElement {
public static demo = () => html`<dees-form-submit>This is a sloted text</dees-form-submit>`; public static demo = demoFunc;
@property({ @property({
type: Boolean, type: Boolean,
@ -39,17 +38,17 @@ export class DeesFormSubmit extends DeesElement {
constructor() { constructor() {
super(); super();
} }
public static styles = [cssManager.defaultStyles, css``]; public static styles = [cssManager.defaultStyles, css``];
public render() { public render() {
return html` return html`
<dees-button <dees-button
status=${this.status} status="${this.status}"
@click=${this.submit} @click="${this.submit}"
.disabled=${this.disabled} ?disabled="${this.disabled}"
.text=${this.text ? this.text : this.textContent}
> >
${this.text || html`<slot></slot>`}
</dees-button> </dees-button>
`; `;
} }
@ -59,13 +58,15 @@ export class DeesFormSubmit extends DeesElement {
return; return;
} }
const parentElement: DeesForm = this.parentElement as DeesForm; const parentElement: DeesForm = this.parentElement as DeesForm;
parentElement.gatherAndDispatch(); if (parentElement && parentElement.gatherAndDispatch) {
parentElement.gatherAndDispatch();
}
} }
public async focus() { public async focus() {
const domtools = await this.domtoolsPromise; const domtools = await this.domtoolsPromise;
if (!this.disabled) { if (!this.disabled) {
domtools.convenience.smartdelay.delayFor(0); await domtools.convenience.smartdelay.delayFor(0);
this.submit(); this.submit();
} }
} }

View File

@ -15,234 +15,173 @@ export const demoFunc = () => html`
margin: 0 auto; margin: 0 auto;
} }
.demo-section { dees-panel {
background: #f8f9fa; margin-bottom: 24px;
border-radius: 8px;
padding: 24px;
} }
@media (prefers-color-scheme: dark) { dees-panel:last-child {
.demo-section { margin-bottom: 0;
background: #1a1a1a;
}
}
.demo-section h3 {
margin-top: 0;
margin-bottom: 16px;
color: #0069f2;
font-size: 18px;
}
.demo-section p {
margin-top: 0;
margin-bottom: 16px;
color: #666;
font-size: 14px;
}
@media (prefers-color-scheme: dark) {
.demo-section p {
color: #999;
}
}
.form-container {
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
@media (prefers-color-scheme: dark) {
.form-container {
background: #222;
border-color: #333;
}
}
.horizontal-form {
display: flex;
align-items: flex-start;
gap: 16px;
flex-wrap: wrap;
} }
`} `}
</style> </style>
<div class="demo-container"> <div class="demo-container">
<div class="demo-section"> <dees-panel .heading="Complete Form Example" .description="A comprehensive form with various input types, validation, and form submission handling">
<h3>Complete Form Example</h3> <dees-form
<p>A comprehensive form with various input types, validation, and form submission handling</p> @formData=${async (eventArg) => {
const form: DeesForm = eventArg.currentTarget;
<div class="form-container"> form.setStatus('pending', 'Processing...');
<dees-form await domtools.plugins.smartdelay.delayFor(2000);
@formData=${async (eventArg) => { form.setStatus('success', 'Form submitted successfully!');
const form: DeesForm = eventArg.currentTarget; await domtools.plugins.smartdelay.delayFor(2000);
form.setStatus('pending', 'Processing...'); form.reset();
await domtools.plugins.smartdelay.delayFor(2000); }}
form.setStatus('success', 'Form submitted successfully!'); >
await domtools.plugins.smartdelay.delayFor(2000); <dees-input-text
form.reset(); .required=${true}
}} key="firstName"
> label="First Name"
<dees-input-text .description=${'Your given name'}
.required=${true} ></dees-input-text>
key="firstName"
label="First Name" <dees-input-text
.description=${'Your given name'} .required=${true}
></dees-input-text> key="lastName"
label="Last Name"
<dees-input-text ></dees-input-text>
.required=${true}
key="lastName" <dees-input-text
label="Last Name" .required=${true}
></dees-input-text> key="email"
label="Email Address"
<dees-input-text .description=${'We will use this to contact you'}
.required=${true} ></dees-input-text>
key="email"
label="Email Address" <dees-input-dropdown
.description=${'We will use this to contact you'} .required=${true}
></dees-input-text> key="country"
.label=${'Country'}
<dees-input-dropdown .options=${[
.required=${true} { option: 'United States', key: 'us' },
key="country" { option: 'Canada', key: 'ca' },
.label=${'Country'} { option: 'Germany', key: 'de' },
.options=${[ { option: 'France', key: 'fr' },
{ option: 'United States', key: 'us' }, { option: 'United Kingdom', key: 'uk' },
{ option: 'Canada', key: 'ca' }, ]}
{ option: 'Germany', key: 'de' }, ></dees-input-dropdown>
{ option: 'France', key: 'fr' },
{ option: 'United Kingdom', key: 'uk' }, <dees-input-text
]} .required=${true}
></dees-input-dropdown> key="password"
label="Password"
<dees-input-text isPasswordBool
.required=${true} .description=${'Minimum 8 characters'}
key="password" ></dees-input-text>
label="Password"
isPasswordBool <dees-input-checkbox
.description=${'Minimum 8 characters'} .required=${true}
></dees-input-text> key="terms"
label="I agree to the Terms and Conditions"
<dees-input-checkbox ></dees-input-checkbox>
.required=${true}
key="terms" <dees-input-checkbox
label="I agree to the Terms and Conditions" key="newsletter"
></dees-input-checkbox> label="Send me promotional emails"
.value=${true}
<dees-input-checkbox ></dees-input-checkbox>
key="newsletter"
label="Send me promotional emails" <dees-form-submit>Create Account</dees-form-submit>
.value=${true} </dees-form>
></dees-input-checkbox> </dees-panel>
<dees-form-submit>Create Account</dees-form-submit>
</dees-form>
</div>
</div>
<div class="demo-section"> <dees-panel .heading="Horizontal Form Layout" .description="Compact form with inputs arranged horizontally - perfect for filters and quick forms">
<h3>Horizontal Form Layout</h3> <dees-form horizontal-layout>
<p>Compact form with inputs arranged horizontally - perfect for filters and quick forms</p> <dees-input-text
key="search"
<div class="form-container"> label="Search"
<dees-form horizontal-layout> ></dees-input-text>
<dees-input-text
key="search" <dees-input-dropdown
label="Search" key="category"
></dees-input-text> .label=${'Category'}
.enableSearch=${false}
<dees-input-dropdown .options=${[
key="category" { option: 'All', key: 'all' },
.label=${'Category'} { option: 'Products', key: 'products' },
.enableSearch=${false} { option: 'Services', key: 'services' },
.options=${[ { option: 'Support', key: 'support' },
{ option: 'All', key: 'all' }, ]}
{ option: 'Products', key: 'products' }, ></dees-input-dropdown>
{ option: 'Services', key: 'services' },
{ option: 'Support', key: 'support' }, <dees-input-dropdown
]} key="sort"
></dees-input-dropdown> .label=${'Sort By'}
.enableSearch=${false}
<dees-input-dropdown .options=${[
key="sort" { option: 'Newest', key: 'newest' },
.label=${'Sort By'} { option: 'Popular', key: 'popular' },
.enableSearch=${false} { option: 'Price: Low to High', key: 'price_asc' },
.options=${[ { option: 'Price: High to Low', key: 'price_desc' },
{ option: 'Newest', key: 'newest' }, ]}
{ option: 'Popular', key: 'popular' }, ></dees-input-dropdown>
{ option: 'Price: Low to High', key: 'price_asc' },
{ option: 'Price: High to Low', key: 'price_desc' }, <dees-input-checkbox
]} key="inStock"
></dees-input-dropdown> label="In Stock Only"
.value=${true}
<dees-input-checkbox ></dees-input-checkbox>
key="inStock" </dees-form>
label="In Stock Only" </dees-panel>
.value=${true}
></dees-input-checkbox>
</dees-form>
</div>
</div>
<div class="demo-section"> <dees-panel .heading="Advanced Form Features" .description="Form with specialized input types and complex validation">
<h3>Advanced Form Features</h3> <dees-form
<p>Form with specialized input types and complex validation</p> @formData=${async (eventArg) => {
const form: DeesForm = eventArg.currentTarget;
<div class="form-container"> const data = eventArg.detail.data;
<dees-form console.log('Form data:', data);
@formData=${async (eventArg) => { form.setStatus('success', 'Data logged to console!');
const form: DeesForm = eventArg.currentTarget; }}
const data = eventArg.detail.data; >
console.log('Form data:', data); <dees-input-iban
form.setStatus('success', 'Data logged to console!'); key="iban"
}} label="IBAN"
> .required=${true}
<dees-input-iban ></dees-input-iban>
key="iban"
label="IBAN" <dees-input-phone
.required=${true} key="phone"
></dees-input-iban> label="Phone Number"
.required=${true}
<dees-input-phone ></dees-input-phone>
key="phone"
label="Phone Number" <dees-input-multitoggle
.required=${true} key="preferences"
></dees-input-phone> .label=${'Notification Preferences'}
.options=${['Email', 'SMS', 'Push', 'In-App']}
<dees-input-multitoggle .selectedOption=${'Email'}
key="preferences" ></dees-input-multitoggle>
.label=${'Notification Preferences'}
.options=${['Email', 'SMS', 'Push', 'In-App']} <dees-input-multiselect
.selectedOption=${'Email'} key="interests"
></dees-input-multitoggle> .label=${'Areas of Interest'}
.options=${[
<dees-input-multiselect { option: 'Technology', key: 'tech' },
key="interests" { option: 'Design', key: 'design' },
.label=${'Areas of Interest'} { option: 'Business', key: 'business' },
.options=${[ { option: 'Marketing', key: 'marketing' },
{ option: 'Technology', key: 'tech' }, { option: 'Sales', key: 'sales' },
{ option: 'Design', key: 'design' }, ]}
{ option: 'Business', key: 'business' }, ></dees-input-multiselect>
{ option: 'Marketing', key: 'marketing' },
{ option: 'Sales', key: 'sales' }, <dees-input-fileupload
]} key="documents"
></dees-input-multiselect> .label=${'Upload Documents'}
.description=${'PDF, DOC, or DOCX files up to 10MB'}
<dees-input-fileupload ></dees-input-fileupload>
key="documents"
.label=${'Upload Documents'} <dees-form-submit>Submit Application</dees-form-submit>
.description=${'PDF, DOC, or DOCX files up to 10MB'} </dees-form>
></dees-input-fileupload> </dees-panel>
<dees-form-submit>Submit Application</dees-form-submit>
</dees-form>
</div>
</div>
</div> </div>
</dees-demowrapper> </dees-demowrapper>
`; `;

View File

@ -132,12 +132,31 @@ export class DeesForm extends DeesElement {
public async collectFormData() { public async collectFormData() {
const children = this.getFormElements(); const children = this.getFormElements();
const valueObject: { [key: string]: string | number | boolean | any[] | File[] | { option: string; key: string; payload?: any } } = {}; const valueObject: { [key: string]: string | number | boolean | any[] | File[] | { option: string; key: string; payload?: any } } = {};
const radioGroups = new Map<string, DeesInputRadio[]>();
for (const child of children) { for (const child of children) {
if (!child.key) { if (!child.key) {
console.log(`form element with label "${child.label}" has no key. skipping.`); console.log(`form element with label "${child.label}" has no key. skipping.`);
continue;
}
// Handle radio buttons specially
if (child instanceof DeesInputRadio && child.name) {
if (!radioGroups.has(child.name)) {
radioGroups.set(child.name, []);
}
radioGroups.get(child.name).push(child);
} else {
valueObject[child.key] = child.value;
} }
valueObject[child.key] = child.value;
} }
// Process radio groups - use the name as key and selected radio's key as value
for (const [groupName, radios] of radioGroups) {
const selectedRadio = radios.find(radio => radio.value === true);
valueObject[groupName] = selectedRadio ? selectedRadio.key : null;
}
return valueObject; return valueObject;
} }

View File

@ -3,33 +3,7 @@ import '@design.estate/dees-wcctools/demotools';
import type { DeesInputRadio } from './dees-input-radio.js'; import type { DeesInputRadio } from './dees-input-radio.js';
export const demoFunc = () => html` export const demoFunc = () => html`
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => { <dees-demowrapper>
// Implement radio group behavior
const radioGroups = new Map<string, DeesInputRadio[]>();
// Group radios by their container
const radioContainers = elementArg.querySelectorAll('.radio-group');
radioContainers.forEach((container) => {
const radios = Array.from(container.querySelectorAll('dees-input-radio')) as DeesInputRadio[];
const groupName = container.getAttribute('data-group') || 'default';
radioGroups.set(groupName, radios);
// Add click handlers for radio group behavior
radios.forEach((radio) => {
radio.addEventListener('click', () => {
if (!radio.disabled && !radio.value) {
// Uncheck all other radios in the group
radios.forEach((r) => {
if (r !== radio) {
r.value = false;
}
});
radio.value = true;
}
});
});
});
}}>
<style> <style>
${css` ${css`
.demo-container { .demo-container {
@ -121,37 +95,43 @@ export const demoFunc = () => html`
<h3>Basic Radio Groups</h3> <h3>Basic Radio Groups</h3>
<p>Radio buttons for single-choice selections</p> <p>Radio buttons for single-choice selections</p>
<div class="radio-group" data-group="plan"> <div class="radio-group">
<div class="radio-group-title">Select your subscription plan:</div> <div class="radio-group-title">Select your subscription plan:</div>
<dees-input-radio <dees-input-radio
.label=${'Basic Plan - $9/month'} .label=${'Basic Plan - $9/month'}
.value=${true} .value=${true}
.key=${'plan-basic'} .key=${'plan-basic'}
.name=${'plan'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'Pro Plan - $29/month'} .label=${'Pro Plan - $29/month'}
.key=${'plan-pro'} .key=${'plan-pro'}
.name=${'plan'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'Enterprise Plan - $99/month'} .label=${'Enterprise Plan - $99/month'}
.key=${'plan-enterprise'} .key=${'plan-enterprise'}
.name=${'plan'}
></dees-input-radio> ></dees-input-radio>
</div> </div>
<div class="radio-group" data-group="priority"> <div class="radio-group">
<div class="radio-group-title">Task Priority:</div> <div class="radio-group-title">Task Priority:</div>
<dees-input-radio <dees-input-radio
.label=${'High Priority'} .label=${'High Priority'}
.key=${'priority-high'} .key=${'priority-high'}
.name=${'priority'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'Medium Priority'} .label=${'Medium Priority'}
.value=${true} .value=${true}
.key=${'priority-medium'} .key=${'priority-medium'}
.name=${'priority'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'Low Priority'} .label=${'Low Priority'}
.key=${'priority-low'} .key=${'priority-low'}
.name=${'priority'}
></dees-input-radio> ></dees-input-radio>
</div> </div>
</div> </div>
@ -160,43 +140,49 @@ export const demoFunc = () => html`
<h3>Horizontal Layout</h3> <h3>Horizontal Layout</h3>
<p>Radio buttons arranged horizontally for yes/no questions</p> <p>Radio buttons arranged horizontally for yes/no questions</p>
<div class="radio-group" data-group="agreement" style="flex-direction: row;"> <div class="radio-group" style="flex-direction: row;">
<div style="margin-right: 16px;">Do you agree?</div> <div style="margin-right: 16px;">Do you agree?</div>
<dees-input-radio <dees-input-radio
.label=${'Yes'} .label=${'Yes'}
.layoutMode=${'horizontal'} .layoutMode=${'horizontal'}
.value=${true} .value=${true}
.key=${'agree-yes'} .key=${'agree-yes'}
.name=${'agreement'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'No'} .label=${'No'}
.layoutMode=${'horizontal'} .layoutMode=${'horizontal'}
.key=${'agree-no'} .key=${'agree-no'}
.name=${'agreement'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'Maybe'} .label=${'Maybe'}
.layoutMode=${'horizontal'} .layoutMode=${'horizontal'}
.key=${'agree-maybe'} .key=${'agree-maybe'}
.name=${'agreement'}
></dees-input-radio> ></dees-input-radio>
</div> </div>
<div class="radio-group" data-group="experience" style="flex-direction: row;"> <div class="radio-group" style="flex-direction: row;">
<div style="margin-right: 16px;">Experience Level:</div> <div style="margin-right: 16px;">Experience Level:</div>
<dees-input-radio <dees-input-radio
.label=${'Beginner'} .label=${'Beginner'}
.layoutMode=${'horizontal'} .layoutMode=${'horizontal'}
.key=${'exp-beginner'} .key=${'exp-beginner'}
.name=${'experience'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'Intermediate'} .label=${'Intermediate'}
.layoutMode=${'horizontal'} .layoutMode=${'horizontal'}
.value=${true} .value=${true}
.key=${'exp-intermediate'} .key=${'exp-intermediate'}
.name=${'experience'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'Expert'} .label=${'Expert'}
.layoutMode=${'horizontal'} .layoutMode=${'horizontal'}
.key=${'exp-expert'} .key=${'exp-expert'}
.name=${'experience'}
></dees-input-radio> ></dees-input-radio>
</div> </div>
</div> </div>
@ -206,22 +192,22 @@ export const demoFunc = () => html`
<p>Multiple radio groups in a survey format</p> <p>Multiple radio groups in a survey format</p>
<div class="grid-layout"> <div class="grid-layout">
<div class="radio-group" data-group="satisfaction"> <div class="radio-group">
<div class="radio-group-title">How satisfied are you?</div> <div class="radio-group-title">How satisfied are you?</div>
<dees-input-radio .label=${'Very Satisfied'} .key=${'sat-very'}></dees-input-radio> <dees-input-radio .label=${'Very Satisfied'} .key=${'sat-very'} .name=${'satisfaction'}></dees-input-radio>
<dees-input-radio .label=${'Satisfied'} .value=${true} .key=${'sat-normal'}></dees-input-radio> <dees-input-radio .label=${'Satisfied'} .value=${true} .key=${'sat-normal'} .name=${'satisfaction'}></dees-input-radio>
<dees-input-radio .label=${'Neutral'} .key=${'sat-neutral'}></dees-input-radio> <dees-input-radio .label=${'Neutral'} .key=${'sat-neutral'} .name=${'satisfaction'}></dees-input-radio>
<dees-input-radio .label=${'Dissatisfied'} .key=${'sat-dis'}></dees-input-radio> <dees-input-radio .label=${'Dissatisfied'} .key=${'sat-dis'} .name=${'satisfaction'}></dees-input-radio>
<dees-input-radio .label=${'Very Dissatisfied'} .key=${'sat-verydis'}></dees-input-radio> <dees-input-radio .label=${'Very Dissatisfied'} .key=${'sat-verydis'} .name=${'satisfaction'}></dees-input-radio>
</div> </div>
<div class="radio-group" data-group="recommend"> <div class="radio-group">
<div class="radio-group-title">Would you recommend us?</div> <div class="radio-group-title">Would you recommend us?</div>
<dees-input-radio .label=${'Definitely'} .key=${'rec-def'}></dees-input-radio> <dees-input-radio .label=${'Definitely'} .key=${'rec-def'} .name=${'recommend'}></dees-input-radio>
<dees-input-radio .label=${'Probably'} .value=${true} .key=${'rec-prob'}></dees-input-radio> <dees-input-radio .label=${'Probably'} .value=${true} .key=${'rec-prob'} .name=${'recommend'}></dees-input-radio>
<dees-input-radio .label=${'Not Sure'} .key=${'rec-unsure'}></dees-input-radio> <dees-input-radio .label=${'Not Sure'} .key=${'rec-unsure'} .name=${'recommend'}></dees-input-radio>
<dees-input-radio .label=${'Probably Not'} .key=${'rec-probnot'}></dees-input-radio> <dees-input-radio .label=${'Probably Not'} .key=${'rec-probnot'} .name=${'recommend'}></dees-input-radio>
<dees-input-radio .label=${'Definitely Not'} .key=${'rec-defnot'}></dees-input-radio> <dees-input-radio .label=${'Definitely Not'} .key=${'rec-defnot'} .name=${'recommend'}></dees-input-radio>
</div> </div>
</div> </div>
</div> </div>
@ -230,26 +216,30 @@ export const demoFunc = () => html`
<h3>States</h3> <h3>States</h3>
<p>Different radio button states</p> <p>Different radio button states</p>
<div class="radio-group" data-group="states"> <div class="radio-group">
<dees-input-radio <dees-input-radio
.label=${'Normal Radio'} .label=${'Normal Radio'}
.key=${'state-normal'} .key=${'state-normal'}
.name=${'states'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'Selected Radio'} .label=${'Selected Radio'}
.value=${true} .value=${true}
.key=${'state-selected'} .key=${'state-selected'}
.name=${'states'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'Disabled Unchecked'} .label=${'Disabled Unchecked'}
.disabled=${true} .disabled=${true}
.key=${'state-disabled1'} .key=${'state-disabled1'}
.name=${'states2'}
></dees-input-radio> ></dees-input-radio>
<dees-input-radio <dees-input-radio
.label=${'Disabled Checked'} .label=${'Disabled Checked'}
.disabled=${true} .disabled=${true}
.value=${true} .value=${true}
.key=${'state-disabled2'} .key=${'state-disabled2'}
.name=${'states2'}
></dees-input-radio> ></dees-input-radio>
</div> </div>
</div> </div>
@ -258,18 +248,18 @@ export const demoFunc = () => html`
<h3>Settings Example</h3> <h3>Settings Example</h3>
<p>Common radio button patterns in settings</p> <p>Common radio button patterns in settings</p>
<div class="radio-group" data-group="theme"> <div class="radio-group">
<div class="radio-group-title">Theme Preference:</div> <div class="radio-group-title">Theme Preference:</div>
<dees-input-radio .label=${'Light Theme'} .key=${'theme-light'}></dees-input-radio> <dees-input-radio .label=${'Light Theme'} .key=${'theme-light'} .name=${'theme'}></dees-input-radio>
<dees-input-radio .label=${'Dark Theme'} .value=${true} .key=${'theme-dark'}></dees-input-radio> <dees-input-radio .label=${'Dark Theme'} .value=${true} .key=${'theme-dark'} .name=${'theme'}></dees-input-radio>
<dees-input-radio .label=${'System Default'} .key=${'theme-system'}></dees-input-radio> <dees-input-radio .label=${'System Default'} .key=${'theme-system'} .name=${'theme'}></dees-input-radio>
</div> </div>
<div class="radio-group" data-group="notifications"> <div class="radio-group">
<div class="radio-group-title">Notification Frequency:</div> <div class="radio-group-title">Notification Frequency:</div>
<dees-input-radio .label=${'All Notifications'} .key=${'notif-all'}></dees-input-radio> <dees-input-radio .label=${'All Notifications'} .key=${'notif-all'} .name=${'notifications'}></dees-input-radio>
<dees-input-radio .label=${'Important Only'} .value=${true} .key=${'notif-important'}></dees-input-radio> <dees-input-radio .label=${'Important Only'} .value=${true} .key=${'notif-important'} .name=${'notifications'}></dees-input-radio>
<dees-input-radio .label=${'None'} .key=${'notif-none'}></dees-input-radio> <dees-input-radio .label=${'None'} .key=${'notif-none'} .name=${'notifications'}></dees-input-radio>
</div> </div>
</div> </div>
</div> </div>

View File

@ -17,6 +17,8 @@ export class DeesInputRadio extends DeesInputBase<DeesInputRadio> {
@property() @property()
public value: boolean = false; public value: boolean = false;
@property({ type: String })
public name: string = '';
constructor() { constructor() {
super(); super();
@ -95,7 +97,27 @@ export class DeesInputRadio extends DeesInputBase<DeesInputRadio> {
} }
public async toggleSelected () { public async toggleSelected () {
this.value = !this.value; // Radio buttons can only be selected, not deselected by clicking
if (this.value) {
return;
}
// If this radio has a name, find and deselect other radios in the same group
if (this.name) {
// Try to find a form container first, then fall back to document
const container = this.closest('dees-form') ||
this.closest('dees-demowrapper') ||
this.closest('.radio-group')?.parentElement ||
document;
const allRadios = container.querySelectorAll(`dees-input-radio[name="${this.name}"]`);
allRadios.forEach((radio: DeesInputRadio) => {
if (radio !== this && radio.value) {
radio.value = false;
}
});
}
this.value = true;
this.dispatchEvent(new CustomEvent('newValue', { this.dispatchEvent(new CustomEvent('newValue', {
detail: this.value, detail: this.value,
bubbles: true bubbles: true

View File

@ -100,7 +100,8 @@ export class DeesInputText extends DeesInputBase {
input:focus { input:focus {
outline: none; outline: none;
border-bottom: 1px solid ${cssManager.bdTheme( colors.bright.blueActive, colors.dark.blueActive)}; border-bottom: 1px solid
${cssManager.bdTheme(colors.bright.blueActive, colors.dark.blueActive)};
cursor: text; cursor: text;
} }
@ -117,6 +118,7 @@ export class DeesInputText extends DeesInputBase {
padding: 4px 0px; padding: 4px 0px;
width: 40px; width: 40px;
z-index: 3; z-index: 3;
text-align: center;
} }
.showPassword:hover { .showPassword:hover {
@ -146,18 +148,20 @@ export class DeesInputText extends DeesInputBase {
letter-spacing: ${this.isPasswordBool ? '1px' : 'normal'}; letter-spacing: ${this.isPasswordBool ? '1px' : 'normal'};
color: ${this.goBright ? '#333' : '#ccc'}; color: ${this.goBright ? '#333' : '#ccc'};
} }
${this.validationText ? css` ${this.validationText
.validationContainer { ? css`
height: 22px; .validationContainer {
opacity: 1; height: 22px;
} opacity: 1;
` : css` }
.validationContainer { `
height: 4px; : css`
padding: 2px !important; .validationContainer {
opacity: 0; height: 4px;
} padding: 2px !important;
`} opacity: 0;
}
`}
</style> </style>
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${this.label} .description=${this.description}></dees-label> <dees-label .label=${this.label} .description=${this.description}></dees-label>
@ -168,9 +172,7 @@ export class DeesInputText extends DeesInputBase {
@input="${this.updateValue}" @input="${this.updateValue}"
.disabled=${this.disabled} .disabled=${this.disabled}
/> />
<div class="validationContainer"> <div class="validationContainer">${this.validationText}</div>
${this.validationText}
</div>
${this.isPasswordBool ${this.isPasswordBool
? html` ? html`
<div class="showPassword" @click=${this.togglePasswordView}> <div class="showPassword" @click=${this.togglePasswordView}>

View File

@ -8,16 +8,8 @@ import {
type TemplateResult, type TemplateResult,
cssManager, cssManager,
css, css,
unsafeCSS,
type CSSResult,
state,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
// Import components used in template
import './dees-form.js';
import './dees-input-text.js';
import './dees-form-submit.js';
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
'dees-simple-login': DeesSimpleLogin; 'dees-simple-login': DeesSimpleLogin;
@ -126,46 +118,19 @@ export class DeesSimpleLogin extends DeesElement {
public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>): Promise<void> { public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>): Promise<void> {
super.firstUpdated(_changedProperties); super.firstUpdated(_changedProperties);
const domtools = await this.domtoolsPromise;
// Wait a tick to ensure child elements are rendered
await this.updateComplete;
const form = this.shadowRoot.querySelector('dees-form') as any; const form = this.shadowRoot.querySelector('dees-form') as any;
if (form) {
if (!form) { form.addEventListener('formData', (event: CustomEvent) => {
console.error('dees-form element not found in dees-simple-login'); this.dispatchEvent(new CustomEvent('login', {
return; detail: event.detail,
bubbles: true,
composed: true
}));
});
} }
// Check if the form has the readyDeferred property and wait for it
if (form.readyDeferred?.promise) {
try {
await form.readyDeferred.promise;
} catch (error) {
console.error('Error waiting for form ready:', error);
}
}
const username = this.shadowRoot.querySelector('dees-input-text[key="username"]');
const password = this.shadowRoot.querySelector('dees-input-text[key="password"]');
const submit = this.shadowRoot.querySelector('dees-form-submit');
// Add form data listener
form.addEventListener('formData', (event: CustomEvent) => {
this.dispatchEvent(new CustomEvent('login', {
detail: event.detail,
bubbles: true,
composed: true
}));
});
} }
/**
* Dispatches a 'login' event when the form is submitted.
* Event detail structure: { data: { username: string, password: string } }
*/
/** /**
* allows switching to slotted content * allows switching to slotted content
*/ */