This commit is contained in:
Juergen Kunz
2025-06-27 16:20:06 +00:00
parent 82ebd9c556
commit 65aa9f3c06
7 changed files with 818 additions and 571 deletions

View File

@ -69,80 +69,97 @@ export class DeesInputRadiogroup extends DeesInputBase<string | object> {
:host {
display: block;
position: relative;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
}
.maincontainer {
display: flex;
flex-direction: column;
gap: 8px;
gap: 10px;
}
.maincontainer.horizontal {
flex-direction: row;
flex-wrap: wrap;
gap: 16px;
gap: 20px;
}
.radio-option {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 0;
gap: 10px;
padding: 6px 0;
cursor: pointer;
transition: all 0.2s ease;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
user-select: none;
position: relative;
border-radius: 4px;
}
.maincontainer.horizontal .radio-option {
padding: 8px 16px 8px 0;
padding: 6px 20px 6px 0;
}
.radio-option:hover .radio-circle {
border-color: ${cssManager.bdTheme('#0050b9', '#0084ff')};
border-color: ${cssManager.bdTheme('hsl(215 20.2% 65.1%)', 'hsl(215 20.2% 35.1%)')};
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 11.8%)')};
}
.radio-option:hover .radio-label {
color: ${cssManager.bdTheme('#1a1a1a', '#ffffff')};
color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')};
}
.radio-circle {
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid ${cssManager.bdTheme('#999', '#666')};
background: ${cssManager.bdTheme('#fff', '#1a1a1a')};
transition: all 0.2s ease;
border: 2px solid ${cssManager.bdTheme('hsl(215 20.2% 65.1%)', 'hsl(215 20.2% 35.1%)')};
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 30% 6.8%)')};
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.radio-option.selected .radio-circle {
border-color: ${cssManager.bdTheme('#0050b9', '#0084ff')};
background: ${cssManager.bdTheme('#0050b9', '#0084ff')};
border-color: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8%)', 'hsl(213.1 93.9% 67.8%)')};
background: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8%)', 'hsl(213.1 93.9% 67.8%)')};
}
.radio-option.selected .radio-circle::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 8px;
height: 8px;
border-radius: 50%;
background: white;
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 30% 6.8%)')};
transform: scale(0);
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.radio-option.selected .radio-circle::after {
transform: scale(1);
}
.radio-circle:focus-visible {
outline: none;
box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 30% 3.9%)')},
0 0 0 4px ${cssManager.bdTheme('hsl(217.2 91.2% 59.8%)', 'hsl(213.1 93.9% 67.8%)')};
}
.radio-label {
font-size: 14px;
color: ${cssManager.bdTheme('#666', '#999')};
transition: color 0.2s ease;
font-weight: 500;
color: ${cssManager.bdTheme('hsl(215.3 25% 26.7%)', 'hsl(217.9 10.6% 74.9%)')};
transition: color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
letter-spacing: -0.006em;
line-height: 20px;
}
.radio-option.selected .radio-label {
color: ${cssManager.bdTheme('#1a1a1a', '#ffffff')};
font-weight: 500;
color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')};
}
:host([disabled]) .radio-option {
@ -151,40 +168,49 @@ export class DeesInputRadiogroup extends DeesInputBase<string | object> {
}
:host([disabled]) .radio-option:hover .radio-circle {
border-color: ${cssManager.bdTheme('#999', '#666')};
border-color: ${cssManager.bdTheme('hsl(215 20.2% 65.1%)', 'hsl(215 20.2% 35.1%)')};
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 30% 6.8%)')};
}
:host([disabled]) .radio-option:hover .radio-label {
color: ${cssManager.bdTheme('#666', '#999')};
color: ${cssManager.bdTheme('hsl(215.3 25% 26.7%)', 'hsl(217.9 10.6% 74.9%)')};
}
.label-text {
font-size: 14px;
font-weight: 500;
color: ${cssManager.bdTheme('#333', '#ccc')};
margin-bottom: 8px;
color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')};
margin-bottom: 10px;
letter-spacing: -0.006em;
line-height: 20px;
}
.description-text {
font-size: 12px;
color: ${cssManager.bdTheme('#666', '#999')};
margin-top: 8px;
line-height: 1.4;
font-size: 13px;
color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};
margin-top: 10px;
line-height: 1.5;
letter-spacing: -0.003em;
}
/* Validation styles */
:host([validationState="invalid"]) .radio-circle {
border-color: #e74c3c;
border-color: ${cssManager.bdTheme('hsl(0 72.2% 50.6%)', 'hsl(0 62.8% 30.6%)')};
}
:host([validationState="invalid"]) .radio-option.selected .radio-circle {
border-color: ${cssManager.bdTheme('hsl(0 72.2% 50.6%)', 'hsl(0 62.8% 30.6%)')};
background: ${cssManager.bdTheme('hsl(0 72.2% 50.6%)', 'hsl(0 62.8% 30.6%)')};
}
:host([validationState="valid"]) .radio-option.selected .radio-circle {
border-color: #27ae60;
background: #27ae60;
border-color: ${cssManager.bdTheme('hsl(142.1 70.6% 45.3%)', 'hsl(142.1 76.2% 36.3%)')};
background: ${cssManager.bdTheme('hsl(142.1 70.6% 45.3%)', 'hsl(142.1 76.2% 36.3%)')};
}
:host([validationState="warn"]) .radio-option.selected .radio-circle {
border-color: #f39c12;
background: #f39c12;
border-color: ${cssManager.bdTheme('hsl(45.4 93.4% 47.5%)', 'hsl(45.4 93.4% 47.5%)')};
background: ${cssManager.bdTheme('hsl(45.4 93.4% 47.5%)', 'hsl(45.4 93.4% 47.5%)')};
}
/* Override base grid layout for radiogroup to prevent large gaps */
@ -212,8 +238,15 @@ export class DeesInputRadiogroup extends DeesInputBase<string | object> {
<div
class="radio-option ${isSelected ? 'selected' : ''}"
@click="${() => this.selectOption(optionKey)}"
@keydown="${(e: KeyboardEvent) => this.handleKeydown(e, optionKey)}"
>
<div class="radio-circle"></div>
<div
class="radio-circle"
tabindex="${this.disabled ? '-1' : '0'}"
role="radio"
aria-checked="${isSelected}"
aria-label="${optionLabel}"
></div>
<div class="radio-label">${optionLabel}</div>
</div>
`;
@ -292,4 +325,33 @@ export class DeesInputRadiogroup extends DeesInputBase<string | object> {
this.selectedOption = this.getOptionKey(firstOption);
}
}
private handleKeydown(event: KeyboardEvent, optionKey: string) {
if (this.disabled) return;
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
this.selectOption(optionKey);
} else if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {
event.preventDefault();
this.focusNextOption();
} else if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {
event.preventDefault();
this.focusPreviousOption();
}
}
private focusNextOption() {
const radioCircles = Array.from(this.shadowRoot.querySelectorAll('.radio-circle'));
const currentIndex = radioCircles.findIndex(el => el === this.shadowRoot.activeElement);
const nextIndex = (currentIndex + 1) % radioCircles.length;
(radioCircles[nextIndex] as HTMLElement).focus();
}
private focusPreviousOption() {
const radioCircles = Array.from(this.shadowRoot.querySelectorAll('.radio-circle'));
const currentIndex = radioCircles.findIndex(el => el === this.shadowRoot.activeElement);
const prevIndex = currentIndex <= 0 ? radioCircles.length - 1 : currentIndex - 1;
(radioCircles[prevIndex] as HTMLElement).focus();
}
}