@mediquo/elements
v0.9.4
Published
Mediquo elements web components
Maintainers
Readme
Lit custom elements for scheduling, video consultations, chat, and payments. Each feature ships as its own entry point — import only what you need. Components are routing-agnostic: they emit semantic events and leave navigation to the host.
Installation
npm install @mediquo/elementsQuick start
Wrap features with the providers they need, then mount the element:
<script type="module">
import "@mediquo/elements/mq-theme-provider";
import "@mediquo/elements/mq-query-client-provider";
import "@mediquo/elements/mq-schedule";
</script>
<mq-theme-provider theme="mediquo">
<mq-query-client-provider>
<mq-schedule
api-key="YOUR_API_KEY"
token="YOUR_ACCESS_TOKEN"
locale="es_ES"
env="production"
></mq-schedule>
</mq-query-client-provider>
</mq-theme-provider>const schedule = document.querySelector("mq-schedule");
schedule?.addEventListener("appointment-created", (event) => {
const { appointmentId } = (event as CustomEvent).detail;
// host decides where to go next
});
schedule?.addEventListener("back", () => {
// user left the wizard
});Components
| Element | Import | Description |
| --- | --- | --- |
| mq-dashboard | @mediquo/elements/mq-dashboard | Patient home dashboard (hero, next appointment, recent documents, support banner); emits redirect events |
| mq-schedule | @mediquo/elements/mq-schedule | Multi-step appointment booking / reschedule |
| mq-schedule-success | @mediquo/elements/mq-schedule-success | Post-booking confirmation screen |
| mq-my-appointments | @mediquo/elements/mq-my-appointments | Upcoming and past appointments |
| mq-videocall | @mediquo/elements/mq-videocall | Video consultation session |
| mq-chat-room | @mediquo/elements/mq-chat-room | Chat with a professional |
| mq-support-chat | @mediquo/elements/mq-support-chat | Support chat bubble |
| mq-checkout | @mediquo/elements/mq-checkout | Generic payment checkout |
| mq-appointment-checkout | @mediquo/elements/mq-appointment-checkout | Appointment payment |
| mq-immediate-videocall-checkout | @mediquo/elements/mq-immediate-videocall-checkout | Immediate videocall payment |
| mq-apple-pay-checkout | @mediquo/elements/mq-apple-pay-checkout | Apple Pay button |
| mq-google-pay-checkout | @mediquo/elements/mq-google-pay-checkout | Google Pay button |
Most features accept api-key, token, locale, and env when used standalone. See the documentation for per-component attributes and events.
mq-schedule offers no digital wallet in its payment step unless the host opts
in. Add google-pay-enabled or apple-pay-enabled to offer them; each still
requires device support and a gateway that accepts wallets.
<mq-schedule api-key="YOUR_API_KEY" apple-pay-enabled></mq-schedule>In React, the same flags are googlePayEnabled / applePayEnabled on MqSchedule.
Providers
Mount these as ancestors of the features that need them:
| Provider | Import | When |
| --- | --- | --- |
| mq-theme-provider | @mediquo/elements/mq-theme-provider | Always — design tokens |
| mq-query-client-provider | @mediquo/elements/mq-query-client-provider | Data-fetching features (mq-schedule, mq-chat-room, …) |
| mq-socket-provider | @mediquo/elements/mq-socket-provider | Realtime features (mq-chat-room) |
| mq-session-provider | @mediquo/elements/mq-session-provider | Features that read the patient session — mq-dashboard, mq-my-appointments, mq-videocall, mq-appointment-documentation, background sync |
| mq-api-client-provider | @mediquo/elements/mq-api-client-provider | Features configured entirely by context — mq-my-appointments, mq-unread-count-sync, mq-professional-presence-sync — and any subtree you want scoped to one organization |
Set mq-session-provider's session as a property (it's the login-token
response object from your auth exchange, not a string attribute):
<mq-theme-provider theme="mediquo">
<mq-query-client-provider>
<mq-socket-provider>
<mq-session-provider id="session">
<!-- feature elements -->
</mq-session-provider>
</mq-socket-provider>
</mq-query-client-provider>
</mq-theme-provider>
<script type="module">
document.querySelector("#session").session = loginTokenResponse;
</script>In React, pass it as a prop: <MqSessionProvider session={loginTokenResponse}>.
Organizations
api-key identifies the organization an element speaks for. Each element builds
its own API client from the api-key it was given and keeps it for its whole
lifetime, so requests always carry the key that element was configured with —
whatever other elements mount, update or unmount alongside it.
Cached data is scoped by that key too. Two organizations never read each other's
cache, and changing an element's api-key in place shows the new organization's
data, not the previous one's.
Several organizations on one page
Supported, with no extra setup. Give each element its own api-key:
<mq-query-client-provider>
<!-- clinic booking -->
<mq-schedule api-key="CLINIC_KEY" token="..."></mq-schedule>
<!-- your tenant's support chat, at the same time -->
<mq-support-chat api-key="TENANT_KEY" token="..."></mq-support-chat>
</mq-query-client-provider>One mq-query-client-provider for the whole session is fine — the cache is
partitioned by organization inside it. You do not need to remount it, clear the
cache, or key elements by organization.
Elements without an api-key
Upgrading:
mq-my-appointments,mq-unread-count-syncandmq-professional-presence-syncused to inherit whatever organization the last element on the page happened to configure. That is what made two organizations unsafe. Wrap them inmq-api-client-provider— it is the only integration change this release asks for.
Some features take no api-key of their own (mq-my-appointments,
mq-unread-count-sync, mq-professional-presence-sync), and some accept one but
are often embedded (mq-chat-room). Wrap those in mq-api-client-provider to say
which organization the subtree belongs to:
<mq-api-client-provider api-key="CLINIC_KEY" token="...">
<mq-my-appointments></mq-my-appointments>
</mq-api-client-provider>An element inside a provider that is given its own api-key uses its own; one
given only a token binds that token to the organization it inherited. Mount one
provider per organization — they nest and coexist.
Background sync
Two headless elements keep the shared query cache in sync with realtime socket
events, so unread badges and presence indicators update without refetching.
Mount each once, under the query-client + socket providers and inside the
mq-api-client-provider for the organization whose data they keep fresh; they
render nothing:
| Element | Import | Keeps in sync |
| --- | --- | --- |
| mq-unread-count-sync | @mediquo/elements/mq-unread-count-sync | Per-room unread counts (incoming messages) |
| mq-professional-presence-sync | @mediquo/elements/mq-professional-presence-sync | Professional active_absence (presence) |
<mq-query-client-provider>
<mq-socket-provider>
<mq-api-client-provider api-key="YOUR_API_KEY" token="...">
<mq-unread-count-sync></mq-unread-count-sync>
<mq-professional-presence-sync></mq-professional-presence-sync>
<!-- feature elements whose cached data these keep fresh -->
</mq-api-client-provider>
</mq-socket-provider>
</mq-query-client-provider>For a Lit host that prefers wiring the logic into its own element, the
UnreadCountSyncController / ProfessionalPresenceSyncController reactive
controllers are exported from the same subpaths.
Toasts
Mount one <mq-toast-container> and drive it imperatively with
registerToastContainer:
import { registerToastContainer, toast } from "@mediquo/elements/toast";
// Point the toast API at a container that inherits your theme/token context:
registerToastContainer(document.querySelector("mq-toast-container"));
toast.success("Saved");
toast.error("Something went wrong");Theming
mq-theme-provider ships two built-in themes: default and mediquo. Override tokens with custom-tokens, or build a typed theme via @mediquo/elements/theme:
<mq-theme-provider
theme="custom"
custom-tokens="--color-primary: #0b6e4f; --color-background: #f7faf8;"
>
<!-- … -->
</mq-theme-provider>import { createTheme } from "@mediquo/elements/theme";
const theme = createTheme({
"--color-primary": "#0b6e4f",
});Brand themes (e.g. Adeslas) via custom-tokens
@mediquo/elements intentionally ships only the design-system-owned themes
(default, mediquo). Partner brands such as Adeslas are not built into the
package — the host owns the brand and supplies it through custom-tokens.
custom-tokens is raw CSS injected into the provider's shadow root after the
selected theme, so it can override any token the design system exposes. There is
no token a built-in theme can set that custom-tokens cannot — the surface is
complete by construction. Pass the brand's token set as a :host { … }-free list
of custom properties:
<mq-theme-provider
theme="default"
custom-tokens="
--color-primary: #6f2c91;
--color-primary-hover: #5a2475;
--font-titles: 'Adeslas', sans-serif;
/* …the rest of the brand's token overrides… */
"
>
<!-- … -->
</mq-theme-provider>Keep the brand's token values in the host application (or a shared host package),
not in @mediquo/elements. This keeps the published package brand-neutral while
letting integrators render any look on top of it.
Icons
Every icon this package renders is drawn in this repository, on a single 24 grid
at the design system's icon stroke, and inherits currentColor. The only
artwork we do not draw is the Google Pay and Apple Pay brand marks, which their
owners require us to display unmodified.
See src/ui/icons/ICONS.md for the full inventory
and the rules for adding a new icon.
Events
Components describe what the user did, never where to navigate. Listen for the actions you care about and own routing in the host.
Shared contract (also exported from @mediquo/elements/events):
| Event | Detail | Meaning |
| --- | --- | --- |
| mq-back | { source?: string } | Generic back chrome |
| mq-home | — | Go to home |
| mq-view-appointments | { tab: "next" \| "history" } | Open appointments list |
| mq-appointment-book | { specialty?, service?, professional? } | Start booking |
| mq-appointment-reschedule | { appointmentId } | Reschedule |
| mq-appointment-join-videocall | { appointmentId } | Join videocall |
| mq-appointment-view-documentation | { appointmentId } | Open documentation |
| mq-view-documents | — | Open the full documents list (from mq-dashboard) |
| mq-download-file | { url, filename } | Host should persist / download a file |
mq-download-file is emitted by mq-chat-room and also bubbles from mq-support-chat (nested room). Feature-specific events (e.g. appointment-created on mq-schedule) are documented per component.
import { DownloadFileEvent } from "@mediquo/elements/events";
el.addEventListener(DownloadFileEvent.eventName, (event) => {
const { url, filename } = event.detail;
// save or trigger a download
});In React, the same callback is onDownloadFile on MqChatRoom and MqSupportChat:
import { MqSupportChat } from "@mediquo/elements/react/mq-support-chat";
<MqSupportChat
apiKey="TENANT_KEY"
token="..."
onDownloadFile={(event) => {
const { url, filename } = event.detail;
// save or trigger a download
}}
/>Locale and environment
Supported locales: es_ES, en_US, pt_PT, de_DE, ca_ES.
Pass locale as an attribute on the feature element, or call setLocale from @mediquo/elements/i18n.
For non-production backends:
import { setEnv } from "@mediquo/elements/env";
setEnv("development"); // or "production"You can also set env="development" / env="production" on elements that expose it.
TypeScript
Entry points ship with .d.ts files. For modern package exports, prefer:
{
"compilerOptions": {
"moduleResolution": "bundler",
"module": "ESNext"
}
}Custom elements work in any framework that can render HTML. In React / Next.js, dynamic-import the entry points in a client component and attach listeners with addEventListener.
License
ISC
