,
): OpenApiSchema | undefined {
if (!schema) return undefined;
if ('$ref' in schema && schema.$ref) {
const name = schema.$ref.split('/').pop();
return name ? all[name] : undefined;
}
return schema as OpenApiSchema;
}
private fieldType(s: OpenApiSchema): string {
if (s.enum) return 'enum';
if (s.format === 'date') return 'date';
if (s.format === 'time') return 'time';
if (s.format === 'date-time') return 'datetime';
if (s.type === 'integer' || s.type === 'number') return 'number';
return 'text';
}
private setValue(name: string, value: unknown) {
this.values = { ...this.values, [name]: value };
}
private onLocation = (e: Event) => {
const detail = (e as CustomEvent).detail as {
latitude?: number;
longitude?: number;
timezone?: string;
utcOffset?: number;
};
if (detail) {
this.values = {
...this.values,
latitude: detail.latitude,
longitude: detail.longitude,
timezone: detail.timezone ?? detail.utcOffset,
};
}
};
private onSubmit = (e: Event) => {
e.preventDefault();
const missing = this.fields
.filter((f) => f.required)
.filter(
(f) => this.values[f.name] === undefined || this.values[f.name] === '',
);
if (missing.length > 0) {
this.dispatchEvent(
new CustomEvent('roxy-validation-error', {
detail: { missing: missing.map((m) => m.name) },
bubbles: true,
composed: true,
}),
);
return;
}
this.dispatchEvent(
new CustomEvent('roxy-submit', {
detail: { endpoint: this.endpoint, values: this.values },
bubbles: true,
composed: true,
}),
);
};
render() {
if (!this.loaded) {
return html``;
}
if (this.specError) {
return html`
Schema load failed: ${this.specError}
`;
}
const renderField = (f: FieldDef) => {
if (
this.hasLocation &&
(f.name === 'latitude' ||
f.name === 'longitude' ||
f.name === 'timezone')
) {
return nothing;
}
const inputId = `roxy-form-${f.name}`;
return html`
${
f.enum
? html``
: html`
this.setValue(
f.name,
this.coerce(f.type, (e.target as HTMLInputElement).value),
)}
/>`
}
${f.description ? html`${f.description}` : nothing}
`;
};
return html``;
}
private htmlType(t: string): string {
switch (t) {
case 'date':
return 'date';
case 'time':
return 'time';
case 'datetime':
return 'datetime-local';
case 'number':
return 'number';
default:
return 'text';
}
}
private coerce(t: string, v: string): unknown {
if (v === '') return undefined;
if (t === 'number') {
const n = Number(v);
return Number.isFinite(n) ? n : undefined;
}
return v;
}
}
declare global {
interface HTMLElementTagNameMap {
'roxy-endpoint-form': RoxyEndpointForm;
}
}