haori-bootstrap
v0.5.42
Published
Bootstrap-based UI extension library for Haori.js.
Maintainers
Readme
Haori.js Bootstrap
Haori.js Bootstrap is a Bootstrap-based UI extension library for Haori.js.
Version: 0.5.42
Overview
- Official name: Haori.js Bootstrap
- Package identifier: haori-bootstrap
- Upstream Haori.js repository: https://github.com/meibinlab/haori-js
- GitHub repository: https://github.com/meibinlab/haori-js-bootstrap
- Supported Bootstrap version: 5.3.x
- Distributed formats: ESM and IIFE
The npm package has been renamed from haori-js-bootstrap to haori-bootstrap, and the previous package is now deprecated. Existing installations should migrate to the new package name. The GitHub repository name remains unchanged.
The library replaces Haori.js static UI methods such as dialog, confirm, toast, openDialog, closeDialog, addErrorMessage, and clearMessages with Bootstrap-based behavior while keeping existing Procedure integration intact.
Installation
Install from npm:
npm install haori-bootstrapThis package expects Haori.js and Bootstrap CSS/JS to be available in the application.
CDN Usage
Load dependencies in this order for browser direct loading:
- Haori.js
- Bootstrap CSS
- bootstrap.bundle.js
- haori-bootstrap
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/haori.iife.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/haori-bootstrap.iife.js"></script>The IIFE build auto-enables when both window.Haori and window.bootstrap are available.
ESM Usage
The ESM build also auto-enables on import when window.Haori and window.bootstrap are available.
import 'haori-bootstrap';When you need to override default options, call install explicitly.
import { install } from 'haori-bootstrap';
install({
fallbackToNative: true,
runtime: 'demo',
});Public API
| API | Purpose | Return value |
| ---- | ---- | ---- |
| dialog(message) | Informational dialog | Promise |
| confirm(message) | Confirmation dialog | Promise |
| toast(message, level) | Toast notification | Promise |
| openDialog(element) | Open the target's modal (pass the .modal itself or a descendant; a non-.modal element resolves to its nearest ancestor .modal). Managed messages and is-invalid / is-valid state under the modal are cleared before it is shown, so a reopened form starts clean. | Promise |
| closeDialog(element) | Close the target's modal (pass the .modal itself or a descendant; a non-.modal element resolves to its nearest ancestor .modal). When called during the show animation, it waits for the animation to finish and then closes | Promise |
| addErrorMessage(target, message) | Append managed error messages | Promise |
| addMessage(target, message, level?) | Append a level-aware managed message ('error' | 'success' | 'warning' | 'info'). Switches Bootstrap validation classes (is-invalid / is-valid) on re-call. | Promise |
| clearMessages(parentOrTarget) | Remove only managed messages | Promise |
| install(options) | Re-apply Bootstrap-backed Haori with overridden options, including runtime | void |
| uninstall() | Restore the original Haori implementation | void |
install options
| Option | Type | Default | Description |
| ---- | ---- | ---- | ---- |
| toastPosition | 'top-start' \| 'top-center' \| 'top-end' \| 'bottom-start' \| 'bottom-center' \| 'bottom-end' | 'bottom-end' | Toast container position. The new position takes effect the next time a toast is shown. |
| toastDelay | number | Bootstrap default (5000ms) | Auto-hide delay in milliseconds for toast notifications. |
| dialogTitle | string | none (no header) | Header title shown by dialog and confirm. |
| dialogOkLabel | string | 'OK' | Label of the OK button in dialog and confirm. |
| dialogCancelLabel | string | 'Cancel' | Label of the cancel button in confirm. |
The identifying attributes (data-haori-confirm-ok, data-haori-confirm-cancel, data-haori-dialog-ok) do not change with the label, so selectors keep working.
Setting the button labels declaratively
The button labels can be set with HTML attributes alone — no JavaScript is needed to call install().
<!-- On the script tag itself (IIFE build) -->
<script src="/js/haori.iife.js"></script>
<script
src="/js/haori-bootstrap.iife.js"
data-dialog-ok-label="OK"
data-dialog-cancel-label="Abbrechen"
></script><!-- On <html> (or <body>) — this also works when importing the ESM build -->
<html lang="de" data-haori-dialog-ok-label="OK" data-haori-dialog-cancel-label="Abbrechen"></html>- Precedence is:
install()argument > script tag attribute ><html>><body>> default (English). - A label once passed to
install()is kept wheninstall()is called again without it, and is not overridden by the declaration (uninstall()clears it). - The script tag attributes only apply to the IIFE build (
haori-bootstrap.iife.js) loaded with<script src>. When the ESM build is loaded withtype="module"orimport, the HTML specification makes the own tag unavailable, so use the<html>/<body>attributes. <body>attributes require<body>to be parsed at load time. Use<html>or the script tag when loading from<head>.- A blank value (whitespace only) is treated as unspecified and falls back to the default, so a label-less button is never produced.
- Only the two button labels can be declared; pass the other install options as arguments.
Procedure Integration Example
Existing Procedure flows can keep using data-click-* and data-click-*-message attributes.
<button
type="button"
data-click-confirm="deleteUser"
data-click-confirm-message="Delete this user?\nThis action cannot be undone."
>
Delete
</button>
<button
type="button"
data-click-toast="notifySaved"
data-click-toast-message="Saved successfully.\nPlease refresh the list."
>
Show saved toast
</button>- Messages are rendered as plain text, not HTML.
- Literal
\nsequences are normalized to line breaks for dialog, confirm, and toast. - Non-string messages are accepted by
dialog,confirm, andtoast. Falsy values (null,undefined,false,0,'') render as an empty message; anything else is converted withString(). This matches the normalization Haori.js applies to attribute values. addMessageandaddErrorMessagedo not apply that normalization. The value is assigned totextContent, so a literal\nstays as written and0renders as0. They render field-level text where the caller controls the exact string;dialog/confirm/toastrender a whole message body, which is why only those normalize.- Haori.js and Procedure remain responsible for interpreting data-click-* attributes and dispatching static method calls.
Persisting collapse state
Add data-haori-persist="key" to a Bootstrap collapse element to persist its open/closed state in sessionStorage and restore it on the next visit.
<button data-bs-toggle="collapse" data-bs-target="#sideMenu">Menu</button>
<div class="collapse" id="sideMenu" data-haori-persist="side-menu">
...
</div>- Listens to
shown.bs.collapse/hidden.bs.collapseand storesshown/hidden. install()restores existing elements and applies restoration to fragments inserted later (e.g. viadata-import) through aMutationObserver.- On restore it also syncs the associated toggles (
data-bs-toggle="collapse"whosedata-bs-target/hrefpoints to the collapse) — theiraria-expandedandcollapsedclass. Toggle syncing requires the collapse element to have anid. - State is keyed by the attribute value; reusing a key across elements shares their state.
- When storage is unavailable (e.g. private mode) the feature silently disables itself and never throws.
uninstall()removes the listeners and observer.
Passing row context to a shared modal
When you open a single shared Bootstrap modal from per-row buttons (inside data-each) and need the row's context (e.g. a target id or type) inside the modal, use Haori core's declarative data-{event}-copy. No hand-written show.bs.modal listener or per-row modal duplication is required.
data-{event}-copy copies binding values into the target element's scope and runs independently of data-{event}-fetch. When data-{event}-form is absent, the copy source is the trigger element's own / inherited binding data — i.e. the current row scope. data-{event}-copy-params selects which keys to forward (&-separated; a leading ! excludes), and existing keys on the target are preserved while same-named keys are overwritten.
<!-- Row button (inside data-each): open the shared modal and copy the row scope -->
<button
data-bs-toggle="modal"
data-bs-target="#acceptModal"
data-click-copy="#acceptModal"
data-click-copy-params="appealId&targetType"
>
Approve
</button>
<!-- Shared modal: give it data-bind to make it a scope root, then reference copied values -->
<div class="modal" id="acceptModal" data-bind="{}">
<input type="hidden" name="appealId" data-attr-value="{{appealId}}" />
<p>{{ targetType == 'account' ? 'Lift the suspension.' : 'Restore the content.' }}</p>
</div>data-{event}-copy/-copy-params/-copy-sourceare Haori core features (not specific to this package); see the haori-js documentation for full semantics.- Give the shared modal a
data-bind(an empty{}is fine) so it acts as a binding scope root the copied values merge into. - Wording can be branched on the copied keys directly in the modal via
{{ ... }}, so renaming/computing keys is usually unnecessary. - See
demo/modal-copy.htmlfor a working example.
Stable selectors for e2e
The elements rendered by dialog / confirm / toast carry stable identifier attributes that do not depend on wording or locale. In e2e tests (e.g. Playwright), prefer building selectors from these attributes instead of visible text or button names — they survive copy changes and localization.
| Element | Attribute | Value |
| ---- | ---- | ---- |
| Toast root | data-haori-toast | "true" |
| Toast level | data-haori-toast-level | "success" | "warning" | "error" | "info" |
| Toast dismiss button | data-haori-toast-dismiss | "true" |
| Toast container | data-haori-toast-container | "true" |
| Info dialog root | data-haori-dialog | "true" |
| Info dialog OK button | data-haori-dialog-ok | "true" |
| Confirm dialog root | data-haori-confirm | "true" |
| Confirm dialog OK button | data-haori-confirm-ok | "true" |
| Confirm dialog cancel button | data-haori-confirm-cancel | "true" |
| Dialog title | data-haori-dialog-title | "true" |
| Managed message container | data-haori-message-container | "true" |
// Operate the confirm dialog without depending on wording
await page.locator('[data-haori-confirm-ok="true"]').click();
// Assert the toast level
await expect(page.locator('[data-haori-toast="true"]').last())
.toHaveAttribute('data-haori-toast-level', 'success');These attributes are maintained as a stable public contract. Info dialogs (
data-haori-dialog) and confirm dialogs (data-haori-confirm) are distinguished by their root attribute.
Build & Publish
Local verification:
npm install
npm run compile
npm run test
npm run build
npm pack --dry-runRegular release flow:
npm version patch
git push origin main --follow-tagsAfter pushing, publish a GitHub Release from the generated version tag. The release workflow then publishes the package and uploads dist.zip automatically.
Example next patch release after 0.5.42:
# version becomes 0.5.43
npm version patch
git push origin main --follow-tagsCreate and publish the GitHub Release for the pushed tag such as 0.5.43.
Release automation:
npm version patchalso keeps the exportedversionconstant, README examples, CDN demo, and Playwright CDN checks in sync.- publish-on-release.yml builds the package and runs npm publish with npm trusted publishing (OIDC), which also attaches provenance.
- release-archive.yml builds dist/ and uploads dist.zip to the same GitHub Release.
First release only:
- If the current version is not published yet, skip
npm version patchand create/push the tag for that version directly. - Register this repository and
publish-on-release.ymlas a trusted publisher in the npm package settings. Authentication uses OIDC, so no long-lived token is needed.
Example for the first 0.3.1 release after the package rename:
git push origin main
git tag 0.3.1
git push origin 0.3.1First release checklist:
- Confirm the npm package name
haori-bootstrapis still available. - Register the trusted publisher (this repository and
publish-on-release.yml) in the npm package settings. - Run local verification and
npm pack --dry-run. - Push the release tag.
- Publish a GitHub Release from that tag.
Documents
- Japanese README: README.ja.md
- Initial design: [doc/Haori.js Bootstrap初期設計書.md](doc/Haori.js Bootstrap初期設計書.md)
- Browser demo procedure proposal: doc/ブラウザデモ時Procedure挙動仕様提案.md
- Changelog: CHANGELOG.md
Notes
- Bootstrap CSS and JS are provided by the application side.
- Haori.js is treated as a prerequisite and is not bundled into this package.
- Browser direct loading for CDN consumers uses dist/haori-bootstrap.iife.js.
- The published npm entry is dist/haori-bootstrap.js with types at dist/index.d.ts.
- Browser demo transport normalization is discussed in the proposal document above and is not part of the current public API.
