feat: Enhance demo components with new input types and layout options

- Added dropdown and radio input components to the demo for application settings.
- Introduced horizontal layout for display preferences and notification settings.
- Implemented checkbox demo with programmatic selection and clear functionality.
- Created file upload and quantity selector demos with various states and configurations.
- Added comprehensive radio input demo showcasing group behavior and various states.
- Developed text input demo with validation states and advanced features like password visibility.
- Introduced a new panel component for better content organization in demos.
This commit is contained in:
Juergen Kunz
2025-06-19 11:39:16 +00:00
parent 79b1a4ea9f
commit 03315db863
26 changed files with 2547 additions and 333 deletions

View File

@ -1,14 +1,14 @@
import {
customElement,
DeesElement,
type TemplateResult,
state,
html,
domtools,
property,
css,
cssManager,
} from '@design.estate/dees-element';
import { DeesInputBase } from './dees-input-base.js';
import * as colors from './00colors.js'
const { demoFunc } = await import('./dees-input-multitoggle.demo.js');
@ -19,18 +19,9 @@ declare global {
}
@customElement('dees-input-multitoggle')
export class DeesInputMultitoggle extends DeesElement {
export class DeesInputMultitoggle extends DeesInputBase<DeesInputMultitoggle> {
public static demo = demoFunc;
@property({
type: String,
})
public label: string;
@property({
type: String,
})
public description: string;
@property()
type: 'boolean' | 'multi' | 'single' = 'multi';
@ -49,23 +40,38 @@ export class DeesInputMultitoggle extends DeesElement {
@property()
selectedOption: string = '';
@property()
@property({ type: Boolean })
boolValue: boolean = false;
// Add value property for form compatibility
public get value(): string | boolean {
if (this.type === 'boolean') {
return this.selectedOption === this.booleanTrueName;
}
return this.selectedOption;
}
public set value(val: string | boolean) {
if (this.type === 'boolean' && typeof val === 'boolean') {
this.selectedOption = val ? this.booleanTrueName : this.booleanFalseName;
} else {
this.selectedOption = val as string;
}
// Defer indicator update to next frame if component not yet updated
if (this.hasUpdated) {
this.setIndicator();
}
}
public static styles = [
...DeesInputBase.baseStyles,
cssManager.defaultStyles,
css`
:host {
display: block;
color: ${cssManager.bdTheme('#333', '#ccc')};
user-select: none;
margin: 8px 0px 24px 0px;
}
.label {
font-size: 14px;
margin-bottom: 8px;
}
.selections {
position: relative;
@ -76,11 +82,11 @@ export class DeesInputMultitoggle extends DeesElement {
width: min-content;
border-radius: 20px;
height: 32px;
border-top: 1px solid #ffffff10;
border-top: 1px solid ${cssManager.bdTheme('rgba(0,0,0,0.1)', 'rgba(255,255,255,0.1)')};
}
.option {
color: #ccc;
color: ${cssManager.bdTheme('#666', '#999')};
position: relative;
padding: 0px 16px;
line-height: 32px;
@ -93,11 +99,11 @@ export class DeesInputMultitoggle extends DeesElement {
}
.option:hover {
color: #fff;
color: ${cssManager.bdTheme('#333', '#fff')};
}
.option.selected {
color: #fff;
color: ${cssManager.bdTheme('#fff', '#fff')};
}
.indicator {
@ -107,17 +113,23 @@ export class DeesInputMultitoggle extends DeesElement {
left: 4px;
top: 3px;
border-radius: 16px;
background: #0050b9;
min-width: 36px;
background: ${cssManager.bdTheme(colors.bright.blueActive, colors.dark.blueActive)};
min-width: 24px;
transition: all 0.1s ease-in-out;
}
.indicator.no-transition {
transition: none;
}
`,
];
public render(): TemplateResult {
return html`
<dees-label .label=${this.label} .description=${this.description}></dees-label>
<div class="mainbox">
<div class="selections">
<div class="input-wrapper">
<dees-label .label=${this.label} .description=${this.description}></dees-label>
<div class="mainbox">
<div class="selections">
<div class="indicator"></div>
${this.options.map(
(option) =>
@ -125,16 +137,31 @@ export class DeesInputMultitoggle extends DeesElement {
${option}
</div> `
)}
</div>
</div>
</div>
`;
}
public async firstUpdated() {
public async connectedCallback() {
await super.connectedCallback();
// Initialize boolean options early
if (this.type === 'boolean' && this.options.length === 0) {
this.options = [this.booleanTrueName || 'true', this.booleanFalseName || 'false'];
}
}
public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) {
super.firstUpdated(_changedProperties);
// Update boolean options if they changed
if (this.type === 'boolean') {
this.options = [this.booleanTrueName || 'true', this.booleanFalseName || 'false'];
}
this.setIndicator();
// Wait for the next frame to ensure DOM is fully rendered
await this.updateComplete;
requestAnimationFrame(() => {
this.setIndicator();
});
}
public async handleSelection(optionArg: string) {
@ -142,18 +169,57 @@ export class DeesInputMultitoggle extends DeesElement {
this.setIndicator();
}
private indicatorInitialized = false;
public async setIndicator() {
const indicator: HTMLDivElement = this.shadowRoot.querySelector('.indicator');
const selectedIndex = this.options.indexOf(this.selectedOption);
// If no valid selection, hide indicator
if (selectedIndex === -1 || !indicator) {
if (indicator) {
indicator.style.opacity = '0';
}
return;
}
const option: HTMLDivElement = this.shadowRoot.querySelector(
`.option:nth-child(${this.options.indexOf(this.selectedOption) + 2})`
`.option:nth-child(${selectedIndex + 2})`
);
if (indicator && option) {
// Only disable transition for the very first positioning
if (!this.indicatorInitialized) {
indicator.classList.add('no-transition');
this.indicatorInitialized = true;
// Remove the no-transition class after a brief delay
setTimeout(() => {
indicator.classList.remove('no-transition');
}, 50);
}
indicator.style.width = `${option.clientWidth - 8}px`;
indicator.style.left = `${option.offsetLeft + 4}px`;
indicator.style.opacity = '1';
}
setTimeout(() => {
indicator.style.transition = 'all 0.1s';
}, 100);
}
public getValue(): string | boolean {
if (this.type === 'boolean') {
return this.selectedOption === this.booleanTrueName;
}
return this.selectedOption;
}
public setValue(value: string | boolean): void {
if (this.type === 'boolean' && typeof value === 'boolean') {
this.selectedOption = value ? (this.booleanTrueName || 'true') : (this.booleanFalseName || 'false');
} else {
this.selectedOption = value as string;
}
if (this.hasUpdated) {
this.setIndicator();
}
}
}