initial
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import { html } from '@design.estate/dees-element';
|
||||
import { injectCssVariables } from '../../00variables.js';
|
||||
|
||||
export const demoFunc = () => {
|
||||
injectCssVariables();
|
||||
return html`
|
||||
<style>
|
||||
.demo-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.demo-section h3 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 0.875rem;
|
||||
color: var(--dees-muted-foreground);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.demo-grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
max-width: 400px;
|
||||
}
|
||||
.demo-row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Basic Inputs</h3>
|
||||
<div class="demo-grid">
|
||||
<dees-mobile-input
|
||||
placeholder="Enter text..."
|
||||
></dees-mobile-input>
|
||||
|
||||
<dees-mobile-input
|
||||
label="Email Address"
|
||||
type="email"
|
||||
placeholder="you@example.com"
|
||||
></dees-mobile-input>
|
||||
|
||||
<dees-mobile-input
|
||||
label="Password"
|
||||
type="password"
|
||||
placeholder="Enter password"
|
||||
></dees-mobile-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Input Types</h3>
|
||||
<div class="demo-grid">
|
||||
<dees-mobile-input
|
||||
label="Phone Number"
|
||||
type="tel"
|
||||
placeholder="+1 (555) 000-0000"
|
||||
></dees-mobile-input>
|
||||
|
||||
<dees-mobile-input
|
||||
label="Quantity"
|
||||
type="number"
|
||||
placeholder="0"
|
||||
></dees-mobile-input>
|
||||
|
||||
<dees-mobile-input
|
||||
label="Search"
|
||||
type="search"
|
||||
placeholder="Search..."
|
||||
></dees-mobile-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>States</h3>
|
||||
<div class="demo-grid">
|
||||
<dees-mobile-input
|
||||
label="Required Field"
|
||||
placeholder="This field is required"
|
||||
required
|
||||
></dees-mobile-input>
|
||||
|
||||
<dees-mobile-input
|
||||
label="Disabled Input"
|
||||
placeholder="Cannot edit"
|
||||
disabled
|
||||
value="Read only value"
|
||||
></dees-mobile-input>
|
||||
|
||||
<dees-mobile-input
|
||||
label="With Error"
|
||||
placeholder="Enter valid email"
|
||||
type="email"
|
||||
value="invalid-email"
|
||||
error="Please enter a valid email address"
|
||||
></dees-mobile-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Autocomplete</h3>
|
||||
<div class="demo-grid">
|
||||
<dees-mobile-input
|
||||
label="Username"
|
||||
autocomplete="username"
|
||||
placeholder="Enter username"
|
||||
></dees-mobile-input>
|
||||
|
||||
<dees-mobile-input
|
||||
label="Current Password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
placeholder="Enter password"
|
||||
></dees-mobile-input>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
@@ -0,0 +1,228 @@
|
||||
import {
|
||||
DeesElement,
|
||||
css,
|
||||
cssManager,
|
||||
customElement,
|
||||
html,
|
||||
property,
|
||||
type TemplateResult,
|
||||
} from '@design.estate/dees-element';
|
||||
|
||||
import { mobileComponentStyles } from '../../00componentstyles.js';
|
||||
import { demoFunc } from './dees-mobile-input.demo.js';
|
||||
|
||||
export type InputType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'dees-mobile-input': DeesMobileInput;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('dees-mobile-input')
|
||||
export class DeesMobileInput extends DeesElement {
|
||||
public static demo = demoFunc;
|
||||
|
||||
@property({ type: String })
|
||||
accessor type: InputType = 'text';
|
||||
|
||||
@property({ type: String })
|
||||
accessor placeholder: string = '';
|
||||
|
||||
@property({ type: String })
|
||||
accessor value: string = '';
|
||||
|
||||
@property({ type: Boolean })
|
||||
accessor disabled: boolean = false;
|
||||
|
||||
@property({ type: String })
|
||||
accessor id: string = '';
|
||||
|
||||
@property({ type: String })
|
||||
accessor name: string = '';
|
||||
|
||||
@property({ type: String })
|
||||
accessor label: string = '';
|
||||
|
||||
@property({ type: String })
|
||||
accessor error: string = '';
|
||||
|
||||
@property({ type: Boolean })
|
||||
accessor required: boolean = false;
|
||||
|
||||
@property({ type: String })
|
||||
accessor autocomplete: string = '';
|
||||
|
||||
public static styles = [
|
||||
cssManager.defaultStyles,
|
||||
mobileComponentStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--dees-foreground);
|
||||
}
|
||||
|
||||
label .required {
|
||||
color: var(--dees-danger);
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
padding: 0 0.75rem;
|
||||
/* 16px minimum to prevent iOS zoom */
|
||||
font-size: 1rem;
|
||||
line-height: 1.25rem;
|
||||
color: var(--dees-foreground);
|
||||
background: var(--dees-background);
|
||||
border: 1px solid var(--dees-input);
|
||||
border-radius: calc(var(--dees-radius) - 2px);
|
||||
outline: none;
|
||||
transition: all var(--dees-transition-fast);
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
border-color: var(--dees-ring);
|
||||
box-shadow: 0 0 0 2px var(--dees-background), 0 0 0 4px var(--dees-ring);
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
background: var(--dees-muted);
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: var(--dees-muted-foreground);
|
||||
}
|
||||
|
||||
/* Remove number input spinners */
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
/* Error state */
|
||||
:host([error]) input,
|
||||
input.error {
|
||||
border-color: var(--dees-danger);
|
||||
}
|
||||
|
||||
:host([error]) input:focus,
|
||||
input.error:focus {
|
||||
box-shadow: 0 0 0 2px var(--dees-background), 0 0 0 4px var(--dees-danger);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 0.75rem;
|
||||
color: var(--dees-danger);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
private handleInput(e: Event) {
|
||||
const input = e.target as HTMLInputElement;
|
||||
this.value = input.value;
|
||||
this.dispatchEvent(new CustomEvent('input', {
|
||||
detail: { value: input.value },
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
}
|
||||
|
||||
private handleChange(e: Event) {
|
||||
const input = e.target as HTMLInputElement;
|
||||
this.value = input.value;
|
||||
this.dispatchEvent(new CustomEvent('change', {
|
||||
detail: { value: input.value },
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
}
|
||||
|
||||
private handleFocus() {
|
||||
// Emit input-focus for keyboard detection
|
||||
this.dispatchEvent(new CustomEvent('input-focus', {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
}
|
||||
|
||||
private handleBlur() {
|
||||
// Emit input-blur for keyboard detection
|
||||
this.dispatchEvent(new CustomEvent('input-blur', {
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
}
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<div class="input-wrapper">
|
||||
${this.label ? html`
|
||||
<label for=${this.id || 'input'}>
|
||||
${this.label}
|
||||
${this.required ? html`<span class="required">*</span>` : ''}
|
||||
</label>
|
||||
` : ''}
|
||||
<input
|
||||
.type=${this.type}
|
||||
.placeholder=${this.placeholder}
|
||||
.value=${this.value}
|
||||
?disabled=${this.disabled}
|
||||
?required=${this.required}
|
||||
.id=${this.id || 'input'}
|
||||
.name=${this.name}
|
||||
.autocomplete=${this.autocomplete}
|
||||
class=${this.error ? 'error' : ''}
|
||||
@input=${this.handleInput}
|
||||
@change=${this.handleChange}
|
||||
@focus=${this.handleFocus}
|
||||
@blur=${this.handleBlur}
|
||||
/>
|
||||
${this.error ? html`
|
||||
<div class="error-message">${this.error}</div>
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus the input programmatically
|
||||
*/
|
||||
public focus() {
|
||||
const input = this.shadowRoot?.querySelector('input');
|
||||
input?.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Blur the input programmatically
|
||||
*/
|
||||
public blur() {
|
||||
const input = this.shadowRoot?.querySelector('input');
|
||||
input?.blur();
|
||||
}
|
||||
}
|
||||
1
ts_web/elements/00group-input/dees-mobile-input/index.ts
Normal file
1
ts_web/elements/00group-input/dees-mobile-input/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './dees-mobile-input.js';
|
||||
2
ts_web/elements/00group-input/index.ts
Normal file
2
ts_web/elements/00group-input/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
// Input Components
|
||||
export * from './dees-mobile-input/index.js';
|
||||
Reference in New Issue
Block a user