A set of utility classes and functions to be used when creating H5P widgets and content types.
This library provides the global H5P
and H5PEditor
objects. These are directly transferred from window
and will only have a value if the objects exist on window
. This means that even if it seems available from h5p-utils
, H5PEditor
will not be available if not in a Widget/Editor context.
import { H5P, H5PEditor } from "h5p-utils";
const $myDiv = H5P.jQuery("div");
const contentId = H5PEditor.contentId;
To create a valid content type, extend H5PContentType<TParams>
as such:
import { H5PContentType } from "h5p-utils";
type Params = {
person: string;
};
export class MyContentType extends H5PContentType<Params> {
attach($container: JQuery<HTMLElement>): void {
const containerElement = $container.get(0);
if (!containerElement) {
console.error(
"Found no containing element to attach `h5p-content-type` to.",
);
return;
}
const { person } = this.params;
containerElement.appendChild(this.wrapper);
containerElement.classList.add("h5p-content-type");
const div = document.createElement("div");
div.innerText = `Hello ${person ?? "World"}`;
this.wrapper.appendChild(div);
}
}
This content type needs to be registered to be used by H5P. The registerContentType
utility function helps us with that:
import { registerContentType } from "h5p-utils";
// The first parameter in `registerContentType` is what will become
// the content type's name, i. e. the name of the constructor's
// property under `window.H5P`. In this case `H5P.MyContentType`.
registerContentType("MyContentType", MyContentType);
An H5P widget is a class that has certain properties and is found on the global window.H5P
object.
Custom widgets should extend H5PWidget<TField, TParams>
:
import { H5PWidget } from "h5p-types";
type Field = H5PFieldText;
// If the `TField` type parameter is a simple field (text, number,
// boolean, i. e. not group or list), then the `TParam` type
// parameter will be inferred. If it can't be inferred, it will
// default to `unknown`, but it can be overridden.
export class MyWidget extends H5PWidget<Field> {
appendTo($container: JQuery<HTMLElement>): void {
const containerElement = $container.get(0);
if (!containerElement) {
console.error("Found no containing element to attach `MyWidget` to.");
return;
}
const { field, params: name, setValue, wrapper } = this;
wrapper.classList.add("field", `field-name-${field.name}`);
containerElement.appendChild(wrapper);
H5PEditor.processSemanticsChunk(
[this.field],
{},
$(this.wrapper),
this.parent,
);
const labelElement = document.createElement("label");
labelElement.innerText = "Insert name";
const nameInput = document.createElement("input");
nameInput.type = "text";
nameInput.onchange = () => setValue(field, nameInput.value);
if (name) {
nameInput.value = name;
}
labelElement.appendChild(nameInput);
wrapper.appendChild(labelElement);
}
validate(): boolean {
return true;
}
remove(): void {}
}
class MyWidget extends H5PWidget implements IH5PWidget {
appendTo($container: JQuery<HTMLElement>) {
const containerElement = $container.get(0);
if (!containerElement) {
throw new Error("Could not find container element for `MyWidget`");
}
const input = document.createElement("input");
input.addEventListener("change", () =>
this.setValue(this.field, input.value),
);
containerElement.appendChild(input);
}
validate() {
return true;
}
remove() {}
}
The H5PWidget
class infers its Params
type field based on the type of the first Field type argument. By setting H5PWidget<H5PFieldText>
, this.params
is now of type string
. If it was set to H5PFieldBoolean
it would be boolean
, and so on.
class MyWidget extends H5PWidget<H5PFieldText> implements IH5PWidget {
appendTo($container: JQuery<HTMLElement>) {
const containerElement = $container.get(0);
if (!containerElement) {
throw new Error("Could not find container element for `MyWidget`");
}
const input = document.createElement("input");
input.addEventListener("change", () =>
this.setValue(this.field, input.value),
);
containerElement.appendChild(input);
}
validate() {
return true;
}
remove() {}
}
If the field type is H5PFieldGroup
, the type of this.params
can be anything. Therefore, its type is unknown | Record<string, unknown>
for now. We can override the Params type to handle this.
type Field = H5PFieldGroup;
type Params = {
name: string;
age: number;
};
class MyWidget extends H5PWidget<Field, Params> implements IH5PWidget {
appendTo($container: JQuery<HTMLElement>) {
const containerElement = $container.get(0);
if (!containerElement) {
throw new Error("Could not find container element for `MyWidget`");
}
const nameInput = document.createElement("input");
nameInput.addEventListener("change", () =>
this.setValue(this.field, {
...this.params,
name: input.value,
}),
);
const ageInput = document.createElement("input");
ageInput.addEventListener("change", () =>
this.setValue(this.field, {
...this.params,
age: Number.parseInt(input.value),
}),
);
containerElement.appendChild(nameInput);
containerElement.appendChild(ageInput);
}
validate() {
return true;
}
remove() {}
}
To register widgets, use registerWidget
:
import { registerWidget } from "h5p-utils";
// The first parameter is, similar to content types, the name of the widget.
// The second parameter is the name that will be used when using the widget
// in semantics.json.
registerWidget("MyWidget", "my-widget", MyWidget);