feat(catalog): add admin dashboard components
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
|
||||
import { idpElementStyles } from './tokens.js';
|
||||
import './idp-input.js';
|
||||
import './idp-checkbox.js';
|
||||
|
||||
export type TIdpFormStatus = 'idle' | 'pending' | 'success' | 'error';
|
||||
export type TIdpFormData = Record<string, string | boolean>;
|
||||
|
||||
export interface IIdpFormSubmitEventDetail {
|
||||
data: TIdpFormData;
|
||||
form: IdpForm;
|
||||
}
|
||||
|
||||
type TFormControl = HTMLElement & {
|
||||
name?: string;
|
||||
key?: string;
|
||||
value?: string;
|
||||
checked?: boolean;
|
||||
validate?: () => boolean;
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'idp-form': IdpForm;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('idp-form')
|
||||
export class IdpForm extends DeesElement {
|
||||
public static demo = () => html`
|
||||
<idp-form>
|
||||
<idp-input name="email" label="Email" type="email" required></idp-input>
|
||||
<idp-form-submit>Continue</idp-form-submit>
|
||||
</idp-form>
|
||||
`;
|
||||
public static demoGroups = ['idp.global v3 primitives'];
|
||||
|
||||
@property({ type: String, reflect: true })
|
||||
public accessor status: TIdpFormStatus = 'idle';
|
||||
|
||||
@property({ type: String })
|
||||
public accessor statusMessage = '';
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public accessor disabled = false;
|
||||
|
||||
public static styles = [
|
||||
...idpElementStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
::slotted(idp-form-submit) {
|
||||
margin-top: 2px;
|
||||
}
|
||||
.status {
|
||||
padding: 11px 13px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--idp-border);
|
||||
background: var(--idp-muted);
|
||||
color: var(--idp-muted-fg);
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.status.pending {
|
||||
border-color: color-mix(in srgb, var(--idp-accent), transparent 60%);
|
||||
color: var(--idp-accent);
|
||||
}
|
||||
.status.success {
|
||||
border-color: color-mix(in srgb, var(--idp-ok), transparent 60%);
|
||||
color: var(--idp-ok);
|
||||
}
|
||||
.status.error {
|
||||
border-color: color-mix(in srgb, var(--idp-destructive), transparent 60%);
|
||||
color: var(--idp-destructive);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public setStatus(statusArg: TIdpFormStatus, messageArg = '') {
|
||||
this.status = statusArg;
|
||||
this.statusMessage = messageArg;
|
||||
}
|
||||
|
||||
public resetStatus() {
|
||||
this.setStatus('idle', '');
|
||||
}
|
||||
|
||||
public submit() {
|
||||
this.handleSubmit();
|
||||
}
|
||||
|
||||
private getControls() {
|
||||
return Array.from(this.querySelectorAll('idp-input, idp-checkbox')) as TFormControl[];
|
||||
}
|
||||
|
||||
private validateControls() {
|
||||
return this.getControls().every((controlArg) => controlArg.validate ? controlArg.validate() : true);
|
||||
}
|
||||
|
||||
private getFormData(): TIdpFormData {
|
||||
const data: TIdpFormData = {};
|
||||
for (const control of this.getControls()) {
|
||||
const name = control.name || control.key;
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof control.checked === 'boolean') {
|
||||
data[name] = control.checked;
|
||||
} else {
|
||||
data[name] = control.value || '';
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private handleSubmitRequest(eventArg: Event) {
|
||||
eventArg.preventDefault();
|
||||
eventArg.stopPropagation();
|
||||
this.handleSubmit();
|
||||
}
|
||||
|
||||
private handleSubmit(eventArg?: Event) {
|
||||
eventArg?.preventDefault();
|
||||
if (this.disabled || this.status === 'pending') {
|
||||
return;
|
||||
}
|
||||
if (!this.validateControls()) {
|
||||
this.setStatus('error', 'Please check the highlighted fields.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent<IIdpFormSubmitEventDetail>('idp-submit', {
|
||||
detail: {
|
||||
data: this.getFormData(),
|
||||
form: this,
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}));
|
||||
}
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
<form novalidate @submit=${this.handleSubmit} @idp-form-submit-request=${this.handleSubmitRequest}>
|
||||
<slot></slot>
|
||||
${this.statusMessage
|
||||
? html`<div class="status ${this.status}" role="status">${this.statusMessage}</div>`
|
||||
: html``}
|
||||
</form>
|
||||
`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user