npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@coding-blocks/vmc-web-components

v0.2.1

Published

Embeddable HTML web components (React powered) for Vidyamandir Classes forms

Readme

@coding-blocks/vmc-web-components

Embeddable HTML web components for Vidyamandir Classes forms. React on the inside, plain custom elements on the outside — a host page only needs one <script> tag and the element.

Every component in this repo ships in one bundle: dist/vmc-web-components.js — 37.8 kB, ~14 kB gzipped over the wire, with the runtime and the CSS inlined. No peer dependencies, no extra network requests.

The source is written against React, but the build aliases react/react-dom to preact/compat, which is what actually ships — same JSX and hooks, ~5x smaller for a script that loads on other people's pages. Nothing in src/ imports Preact directly, so the alias in vite.config.ts is the only place that knows. Two build notes worth keeping: the published bundle carries no sourcemap (it was 4.5x the size of the code), and terser's unsafe_* / booleans_as_integers options are deliberately off — they saved ~2 kB and broke Preact's event proxy at runtime.

Usage

<script src="https://unpkg.com/@coding-blocks/vmc-web-components"></script>

<vmc-online-lead></vmc-online-lead>

The API base URL is compiled into the bundle — embedding pages never pass it. See Environments.

Pin a version in production:

<script src="https://unpkg.com/@coding-blocks/[email protected]/dist/vmc-web-components.js"></script>

The script can go anywhere — elements already in the DOM upgrade as soon as it loads. Each component renders inside a shadow root, so the host page's CSS can't leak in and the component's CSS can't leak out.

Components

<vmc-online-lead>

The "Want a call back?" lead form. Fields posted to the backend: name, email, dial_code, mobile, form_class_id, form_course_id, state_id, city_id.

All eight visible fields are mandatory — name, email, mobile, class, course, state and city are validated on submit, each showing its own message under the field (Please select a class.) and a red border, plus required / aria-required on the control itself. Course stays disabled until a class is picked and city until a state is picked, and each resets when its parent changes, so an orphaned pair can't be submitted.

Mobile verification is mandatory: Submit stays disabled until the number is OTP-verified and the consent box is ticked. Editing the number after verification drops it back to unverified, so a submission can never carry an OTP that belongs to a different number.

Once verified, the dial code and mobile inputs are disabled, and the verified number is held in verification state. The submit payload is built from that stored value rather than from the inputs, so re-enabling the fields through devtools cannot put an unverified number on the wire.

| Attribute | Default | Purpose | | --- | --- | --- | | api-base | build-time VITE_API_URL | Override the compiled-in base (testing only) | | heading | Want a call back ? | Card heading | | dial-code | +91 | Pre-selected dial code | | consent-text | VMC authorisation copy | Consent checkbox label | | submit-label | Submit | Submit button label | | success-title | Thank you! | Heading after a successful submit | | success-message | counsellor call-back copy | Body after a successful submit | | extra-payload | — | JSON object merged into the submit payload |

Events (bubbling and composed, so they cross the shadow boundary):

const el = document.querySelector('vmc-online-lead')
el.addEventListener('vmc-lead-success', (e) => console.log(e.detail.response))
el.addEventListener('vmc-lead-error', (e) => console.log(e.detail.error))

<vmc-online-leed-neet>

The NEET stream. Identical to <vmc-online-lead> — same fields, validation, OTP flow and payload — with two differences:

  • the class list is fetched narrowed to that category: GET /lookups/form-classes?filter={"category":"NEET"}
  • source defaults to vmc-online-lead-form-neet instead of vmc-online-lead-form (a data-source attribute still wins)

Courses are unchanged: fetched for whichever class is selected, via ?filter={"form_class_id":<id>}.

<vmc-online-leed-neet data-source="neet-landing-page"></vmc-online-leed-neet>

It takes every attribute listed for <vmc-online-lead> above.

Both elements are the same React component behind a Variant (src/components/OnlineLead.tsx) holding the default source and an optional class-list filter — so a new stream is a Variant plus one defineElement call, not a copy of the form.

Environments

The base URL comes from VITE_API_URL, read at build time and inlined into dist/vmc-web-components.js:

| File | Used by | Value | | --- | --- | --- | | .env.development | npm run dev, npm run build:dev | http://localhost:3000/api | | .env.production | npm run build (what gets published) | https://vmcrm-api.codingblocks.com/api |

npm run build always builds in production mode, so npm publish can only ever ship the production base. The build refuses to start if VITE_API_URL is missing or isn't a URL, so a bundle pointing nowhere can't be produced.

Changing the base means a rebuild and a republish — that's the point: embedding pages carry no configuration and can't drift.

Endpoints

Defaults are relative to the compiled-in base. Each is overridable per element with the matching attribute, so a component can be repointed without a rebuild.

| Purpose | Default path | Attribute | | --- | --- | --- | | Classes | GET /lookups/form-classes | classes-path | | Courses | GET /lookups/form-courses | courses-path | | States | GET /lookups/states | states-path | | Cities | GET /lookups/cities | cities-path | | Send OTP | POST /auth/login/send-otp | otp-send-path | | Verify OTP | POST /auth/login/verify-otp | otp-verify-path | | Submit form | POST /forms/vmc-online-lead | submit-path |

Dependent lookups pass a Prisma-shaped where clause as the filter query param, matching the CRM's lookup controller:

GET /lookups/form-courses?filter={"form_class_id":6}
GET /lookups/cities?filter={"state_id":2}

Lookup responses may be either a bare array or { data: [...] }; both are accepted. Items are { id, name }.

OTP flow (same shape as the CRM login flow):

POST {otp-send-path}    { mobile_number, dial_code }        -> { otp_id }
POST {otp-verify-path}  { mobile_number, dial_code, otp_id, otp }  -> 2xx

Submit payload:

{
  "name": "…",
  "email": "…",
  "dial_code": "+91",
  "mobile": "9999999999",
  "form_class_id": 6,
  "form_course_id": 61,
  "state_id": 2,
  "city_id": 21,
  "admission_test_id": 2147483647,
  "otp_id": "…",
  "mobile_verified": true,
  "consent": true,
  "source": "vmc-online-lead-form",
  "page_url": "https://…"
}

source is always vmc-online-lead-form, and admission_test_id is always 2147483647 — the fixed placeholder row for leads that arrive without a test assigned.

Errors are read from { error } (falling back to { message }) on any non-2xx response, so backend validation messages surface directly in the form.

Theming

Override the CSS custom properties on the element — they pierce the shadow root:

<style>
  vmc-online-lead {
    --vmc-primary: #3b3663;
    --vmc-heading: #322c78;
    --vmc-font: 'Poppins', sans-serif;
    --vmc-max-width: 560px;
    --vmc-card-radius: 24px;
    --vmc-field-height: 56px;
  }
</style>

The component does not load webfonts. If the page wants Poppins (the VMC look), include it in the host page and it will be inherited.

Development

npm install
npm run dev        # http://localhost:5173/playground/index.html (.env.development)
npm run build      # production bundle + dist/types (.env.production)
npm run build:dev  # same bundle, pointed at localhost — for testing dist/ locally
npm run typecheck

playground/index.html mounts every component and logs every request plus the events it fires. Two controls at the top:

  • API base override — leave empty to use the compiled-in .env.development base; fill it in to point the component elsewhere.
  • Mode:
    • hybrid (default) — classes, courses and the real OTP flow hit the backend; /lookups/states, /lookups/cities and POST /forms/vmc-online-lead are stubbed, because the first two sit behind requireAuth and the third isn't written yet.
    • live — nothing is stubbed.
    • mock — every endpoint is stubbed, no backend needed. OTP is 123456.

Both can be driven from the URL: ?api=…&mode=mock.

The OTP defaults point at /auth/login/send-otp + /auth/login/verify-otp, which only accept mobiles that already belong to a registered CRM user — an unknown number gets 404 No account found for this mobile. A public lead form will eventually need its own pair; point otp-send-path / otp-verify-path at them when they exist.

Adding a component

  1. Write the React component in src/components/, taking { props, ctx }.
  2. Register it in src/index.ts via defineElement('vmc-…', Component, attrs), listing the attributes that should trigger a re-render.
  3. Mount it in playground/index.html.

It lands in the same single bundle automatically.

Publishing

npm version patch
npm publish            # runs the build via prepublishOnly

Published under the @coding-blocks scope with public access.