gov-gui
Version:
Gov UI Component Library Typscript Build
283 lines (278 loc) • 14.5 kB
JavaScript
import { proxyCustomElement, HTMLElement, createEvent, h } from '@stencil/core/internal/client';
import { g as getGlobalPropsClasses, a as getAnimationClasses } from './animation-helpers.js';
import { d as defineCustomElement$3 } from './gov-button2.js';
import { d as defineCustomElement$2 } from './gov-icon2.js';
const govStepperCss = ".stepper-container{position:relative;padding:20px;border-radius:12px;border:1px solid var(--border-color);box-shadow:0 2px 5px rgba(0, 0, 0, 0.1)}.stepper{display:flex;gap:1rem;margin-bottom:1.5rem}.stepper.horizontal{justify-content:space-between}.stepper.horizontal::before{content:'';position:absolute;top:35px;left:40px;right:40px;height:2px;background-color:#0098DB;z-index:-1}.stepper.vertical{flex-direction:column;align-items:flex-start}.stepper.vertical::before{content:'';position:absolute;top:30px;left:35px;height:calc(100% - 100px);width:2px;background-color:#0098DB;z-index:-1}.step{display:flex;align-items:center;gap:0.5rem}.step-number{width:30px;height:30px;border-radius:50%;background:var(--border-color);display:flex;align-items:center;justify-content:center;font-weight:bold}.step.active .step-number{background:var(--primary-color);color:var(--white-color)}.step.completed .step-number{background:var(--primary-color);color:var(--white-color)}.step-content{margin-top:1rem}.stepper-buttons{display:flex;gap:0.5rem;margin-top:1.5rem}.step-error{margin-top:10px;padding:0.75rem;border-radius:4px;background-color:var(--danger-color);color:var(--white-color);text-align:center}.submit-status{margin-top:1rem;padding:0.75rem;border-radius:4px;display:none;text-align:center}.submit-status.visible{display:block}.status-success{background:var(--success-color);color:var(--white-color)}.status-error{background:var(--error-color);color:var(--white-color)}.loading-overlay{position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(129, 19, 19, 0.8);display:flex;align-items:center;justify-content:center}";
const GovStepperStyle0 = govStepperCss;
const GovStepper$1 = /*@__PURE__*/ proxyCustomElement(class GovStepper extends HTMLElement {
constructor() {
super();
this.__registerHost();
this.__attachShadow();
this.stepChanged = createEvent(this, "stepChanged", 7);
this.stepSubmitted = createEvent(this, "stepSubmitted", 7);
this.stepError = createEvent(this, "stepError", 7);
this.currentStep = 0;
this.steps = [];
this.isSubmitting = false;
this.submitStatus = 'idle';
this.stepErrorMessage = ''; // Holds error messages for the current step
this.variant = 'horizontal';
this.nextText = 'Next';
this.prevText = 'Previous';
this.submitText = 'Submit';
this.resetOnSubmit = true; // Reset on submit by default
this.maxStepsVisible = 5; // For horizontal variant
this.animationDelay = '2s';
this.allClasses = '';
}
handleStepChange(newValue) {
this.stepChanged.emit(newValue);
}
//watching for any change in animations to trigger them
watchAnimations() {
this.provideClass();
}
watchAnimationsDelay() {
this.provideClass();
}
watchAnimationsSpeed() {
this.provideClass();
}
componentWillLoad() {
// Collect all step slots (assuming slots are named step-0, step-1, etc.)
const slotElements = this.el.querySelectorAll('[slot^="step-"]');
this.steps = Array.from(slotElements).map((_, index) => `Step ${index + 1}`);
const animationClasses = getAnimationClasses({
animation: this.animation,
animationDelay: this.animationDelay,
animationSpeed: this.animationSpeed
});
this.allClasses = getGlobalPropsClasses({
classes: ' ' + animationClasses,
});
}
//Called on change of any animation related property to trigger change
provideClass() {
const animationClasses = getAnimationClasses({
animation: this.animation,
animationDelay: this.animationDelay,
animationSpeed: this.animationSpeed
});
this.allClasses = getGlobalPropsClasses({
classes: ' ' + animationClasses,
});
}
async handleStepTransition(direction) {
try {
if (direction === 'next' && this.currentStep < this.steps.length - 1) {
// Validate current step's inputs before moving to next step.
if (!this.areInputsValid(this.currentStep))
return;
if (this.validateStep && !(await this.validateStep(this.currentStep)))
return;
// Clear any previous error messages once validation passes
this.stepErrorMessage = '';
this.currentStep += 1;
}
else if (direction === 'prev' && this.currentStep > 0) {
this.currentStep -= 1;
}
}
catch (error) {
this.submitStatus = 'error';
this.stepError.emit(error);
}
}
async handleSubmit() {
try {
this.isSubmitting = true;
// Collect input values from all custom input components.
const formData = this.collectInputData();
console.log('Collected Data:', formData); // Debugging
// Emit the collected data.
this.stepSubmitted.emit(formData);
// Call onSubmit handler if provided.
if (this.onSubmit) {
await this.onSubmit();
}
// Reset inputs if resetOnSubmit is true.
if (this.resetOnSubmit) {
this.resetInputs();
this.currentStep = 0;
this.submitStatus = 'success';
setTimeout(() => {
this.submitStatus = 'idle'; // Reset status after showing success message.
}, 2000);
}
}
catch (error) {
this.submitStatus = 'error';
this.stepError.emit(error);
}
finally {
this.isSubmitting = false;
}
}
/**
* Helper: Returns the current value from a custom component.
* For gov-input and gov-radiobutton, it attempts to use the public 'value' property;
* for gov-checkbox, it returns the public 'checked' property.
*/
getValueFromComponent(component) {
const comp = component;
const tag = component.tagName.toLowerCase();
if (tag === 'gov-input') {
return comp.value !== undefined ? comp.value : component.getAttribute('value') || '';
}
else if (tag === 'gov-radiobutton') {
return comp.value !== undefined ? comp.value : '';
}
else if (tag === 'gov-checkbox') {
return comp.checked;
}
return null;
}
/**
* Collects input data from all custom components (gov-input, gov-radiobutton, gov-checkbox)
* present in the stepper.
*/
collectInputData() {
const components = this.el.querySelectorAll('gov-input, gov-radiobutton, gov-checkbox');
const formData = {};
components.forEach(component => {
const name = component.getAttribute('name');
if (!name)
return;
const value = this.getValueFromComponent(component);
if (value !== null && value !== undefined) {
// For checkboxes, include only if checked.
if (component.tagName.toLowerCase() === 'gov-checkbox') {
if (value) {
formData[name] = component.getAttribute('value') || 'true';
}
}
else {
formData[name] = String(value);
}
}
});
return formData;
}
/**
* Validates all required fields within the current step.
* This queries for custom elements marked with [required] and uses the helper function
* to determine if they have a non-empty value (or are checked, for checkboxes).
* If there are validation errors, an error message is stored in `stepErrorMessage`.
*/
areInputsValid(stepIndex) {
const stepSlot = this.el.querySelector(`[slot="step-${stepIndex}"]`);
if (!stepSlot)
return false;
// Query custom elements that are marked as required.
const requiredComponents = stepSlot.querySelectorAll('gov-input[required], gov-radiobutton[required], gov-checkbox[required]');
let allValid = true;
const messages = [];
requiredComponents.forEach(component => {
const tag = component.tagName.toLowerCase();
// Use the 'name' attribute for a friendly label.
const name = component.getAttribute('name') || 'This field';
const value = this.getValueFromComponent(component);
if (tag === 'gov-input' || tag === 'gov-radiobutton') {
if (!value || (typeof value === 'string' && value.trim() === '')) {
allValid = false;
messages.push(`${name} is required.`);
}
}
else if (tag === 'gov-checkbox') {
if (!value) {
allValid = false;
messages.push(`You must agree to ${name}.`);
}
}
});
// Update the state error message for display in the step.
this.stepErrorMessage = allValid ? '' : messages.join(' ');
return allValid;
}
/**
* Resets all custom input components by calling their reset() method, if available.
*/
resetInputs() {
const components = this.el.querySelectorAll('gov-input, gov-radiobutton, gov-checkbox');
components.forEach(component => {
if (typeof component.reset === 'function') {
component.reset();
}
});
}
render() {
const isLastStep = this.currentStep === this.steps.length - 1;
const statusClass = this.submitStatus !== 'idle' ? 'visible' : '';
return (h("div", { key: '58d4907362a380fc43d5728faa3d5127637c6246', class: `stepper-container ${this.allClasses}` }, this.isSubmitting && (h("div", { key: '29c734572f77034d4c982dea2e53d57d6b9f0b55', class: "loading-overlay" }, h("gov-spinner", { key: 'c3647643af2ee246a83df3aaed92d4a9e71b6705', size: "lg" }))), h("div", { key: '1e31e5b73200a8db98418abd5a0abee91a45ffe8', class: { stepper: true, [this.variant]: true } }, this.steps.map((_, index) => (h("div", { class: {
step: true,
active: index === this.currentStep,
completed: index < this.currentStep,
[this.variant]: true,
} }, h("div", { class: "step-number" }, index + 1), this.variant === 'vertical' && index === this.currentStep && (h("div", { class: "step-content" }, h("slot", { name: `step-${this.currentStep}` }))))))), this.variant === 'horizontal' && (h("div", { key: 'caa3176e98cfcc54eb133258fdf307a02a8fcb27', class: "step-content" }, h("slot", { key: '3061b1af7f62a87caddff6d74fe795ea3eb2d61d', name: `step-${this.currentStep}` }), this.stepErrorMessage && (h("div", { key: 'c7c759a13313c2c0098887a1dd68b1aa99951964', class: "step-error" }, this.stepErrorMessage)))), h("div", { key: 'b8c03eae81fb47a3e86560eec4afd57a8da6485b', class: "stepper-buttons" }, h("gov-button", { key: '0c6f7dbdba895d7752f3021da060b551f72dbe5e', variant: "white", disabled: this.currentStep === 0 || this.isSubmitting, onClick: () => this.handleStepTransition('prev'), label: this.prevText }), h("gov-button", { key: 'bd903668fc6b44740319049c9787a99aa809b859', variant: "primary", disabled: this.isSubmitting, onClick: isLastStep
? () => this.handleSubmit()
: () => this.handleStepTransition('next'), label: isLastStep ? this.submitText : this.nextText })), h("div", { key: '9feafa94a48fb96b4bfd2b8709af8c6d2c299a69', class: `submit-status ${statusClass} status-${this.submitStatus}` }, h("slot", { key: '11e68977d8b24aa0d641a21ed6c0af5b931d20a2', name: this.submitStatus === 'success' ? 'success-message' : 'error-message' }, this.submitStatus === 'success' && 'Form submitted successfully!', this.submitStatus === 'error' && 'Submission failed. Please try again.'))));
}
get el() { return this; }
static get watchers() { return {
"currentStep": ["handleStepChange"],
"animation": ["watchAnimations"],
"animationDelay": ["watchAnimationsDelay"],
"animationSpeed": ["watchAnimationsSpeed"]
}; }
static get style() { return GovStepperStyle0; }
}, [1, "gov-stepper", {
"variant": [1],
"validateStep": [16],
"onSubmit": [16],
"nextText": [1, "next-text"],
"prevText": [1, "prev-text"],
"submitText": [1, "submit-text"],
"resetOnSubmit": [4, "reset-on-submit"],
"maxStepsVisible": [2, "max-steps-visible"],
"animation": [1],
"animationDelay": [1, "animation-delay"],
"animationSpeed": [1, "animation-speed"],
"currentStep": [32],
"steps": [32],
"isSubmitting": [32],
"submitStatus": [32],
"stepErrorMessage": [32]
}, undefined, {
"currentStep": ["handleStepChange"],
"animation": ["watchAnimations"],
"animationDelay": ["watchAnimationsDelay"],
"animationSpeed": ["watchAnimationsSpeed"]
}]);
function defineCustomElement$1() {
if (typeof customElements === "undefined") {
return;
}
const components = ["gov-stepper", "gov-button", "gov-icon"];
components.forEach(tagName => { switch (tagName) {
case "gov-stepper":
if (!customElements.get(tagName)) {
customElements.define(tagName, GovStepper$1);
}
break;
case "gov-button":
if (!customElements.get(tagName)) {
defineCustomElement$3();
}
break;
case "gov-icon":
if (!customElements.get(tagName)) {
defineCustomElement$2();
}
break;
} });
}
const GovStepper = GovStepper$1;
const defineCustomElement = defineCustomElement$1;
export { GovStepper, defineCustomElement };
//# sourceMappingURL=gov-stepper.js.map