feat: Implement unified input component architecture with standardized margins and layout modes
This commit is contained in:
184
ts_web/elements/dees-input-base.ts
Normal file
184
ts_web/elements/dees-input-base.ts
Normal file
@ -0,0 +1,184 @@
|
||||
import {
|
||||
DeesElement,
|
||||
property,
|
||||
css,
|
||||
type CSSResult,
|
||||
cssManager,
|
||||
} from '@design.estate/dees-element';
|
||||
import * as domtools from '@design.estate/dees-domtools';
|
||||
|
||||
/**
|
||||
* Base class for all dees-input components
|
||||
* Provides unified margin system and layout mode support
|
||||
*/
|
||||
export abstract class DeesInputBase<T = any> extends DeesElement {
|
||||
/**
|
||||
* Layout mode for the input component
|
||||
* - vertical: Traditional form layout (label on top)
|
||||
* - horizontal: Inline layout (label position configurable)
|
||||
* - auto: Detect from parent context
|
||||
*/
|
||||
@property({ type: String })
|
||||
public layoutMode: 'vertical' | 'horizontal' | 'auto' = 'auto';
|
||||
|
||||
/**
|
||||
* Position of the label relative to the input
|
||||
*/
|
||||
@property({ type: String })
|
||||
public labelPosition: 'top' | 'left' | 'right' | 'none' = 'top';
|
||||
|
||||
/**
|
||||
* Common properties for all inputs
|
||||
*/
|
||||
@property({ type: String })
|
||||
public key: string;
|
||||
|
||||
@property({ type: String })
|
||||
public label: string;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public required: boolean = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public disabled: boolean = false;
|
||||
|
||||
@property({ type: String })
|
||||
public description: string;
|
||||
|
||||
/**
|
||||
* Common styles for all input components
|
||||
*/
|
||||
public static get baseStyles(): CSSResult[] {
|
||||
return [
|
||||
css`
|
||||
/* CSS Variables for consistent spacing */
|
||||
: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 */
|
||||
}
|
||||
|
||||
/* Default vertical stacking mode (for forms) */
|
||||
:host {
|
||||
display: block;
|
||||
margin: 0;
|
||||
margin-bottom: var(--dees-input-vertical-gap);
|
||||
}
|
||||
|
||||
/* Last child in container should have no bottom margin */
|
||||
:host(:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Horizontal layout mode - activated by attribute */
|
||||
:host([layout-mode="horizontal"]) {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
margin-right: var(--dees-input-horizontal-gap);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
:host([layout-mode="horizontal"]:last-child) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* Auto mode - inherit from parent dees-form if present */
|
||||
|
||||
/* Label position variations */
|
||||
:host([label-position="left"]) .input-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: var(--dees-input-label-gap);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:host([label-position="right"]) .input-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: var(--dees-input-label-gap);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:host([label-position="top"]) .input-wrapper {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:host([label-position="none"]) dees-label {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Subject for value changes that all inputs should implement
|
||||
*/
|
||||
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject<T>();
|
||||
|
||||
/**
|
||||
* Called when the element is connected to the DOM
|
||||
* Sets up layout mode detection
|
||||
*/
|
||||
async connectedCallback() {
|
||||
await super.connectedCallback();
|
||||
this.detectLayoutMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the appropriate layout mode based on parent context
|
||||
*/
|
||||
private detectLayoutMode() {
|
||||
if (this.layoutMode !== 'auto') {
|
||||
this.setAttribute('layout-mode', this.layoutMode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if parent is a form with horizontal layout
|
||||
const parentForm = this.closest('dees-form');
|
||||
if (parentForm && parentForm.hasAttribute('horizontal-layout')) {
|
||||
this.setAttribute('layout-mode', 'horizontal');
|
||||
} else {
|
||||
this.setAttribute('layout-mode', 'vertical');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the layout mode attribute when property changes
|
||||
*/
|
||||
updated(changedProperties: Map<string, any>) {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has('layoutMode')) {
|
||||
this.detectLayoutMode();
|
||||
}
|
||||
|
||||
if (changedProperties.has('labelPosition')) {
|
||||
this.setAttribute('label-position', this.labelPosition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard method for freezing input (disabling)
|
||||
*/
|
||||
public async freeze() {
|
||||
this.disabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard method for unfreezing input (enabling)
|
||||
*/
|
||||
public async unfreeze() {
|
||||
this.disabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method that child classes must implement to get their value
|
||||
*/
|
||||
public abstract getValue(): any;
|
||||
|
||||
/**
|
||||
* Abstract method that child classes must implement to set their value
|
||||
*/
|
||||
public abstract setValue(value: any): void;
|
||||
}
|
Reference in New Issue
Block a user