@pagefind/component-ui 1.5.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -0
- package/components/base-element.ts +110 -0
- package/components/index.ts +31 -0
- package/components/instance-manager.ts +91 -0
- package/components/pagefind-config.ts +44 -0
- package/components/pagefind-filter-dropdown.ts +702 -0
- package/components/pagefind-filter-pane.ts +525 -0
- package/components/pagefind-input.ts +224 -0
- package/components/pagefind-keyboard-hints.ts +62 -0
- package/components/pagefind-modal-body.ts +19 -0
- package/components/pagefind-modal-footer.ts +16 -0
- package/components/pagefind-modal-header.ts +59 -0
- package/components/pagefind-modal-trigger.ts +195 -0
- package/components/pagefind-modal.ts +209 -0
- package/components/pagefind-results.ts +586 -0
- package/components/pagefind-searchbox.ts +888 -0
- package/components/pagefind-summary.ts +138 -0
- package/core/announcer.ts +134 -0
- package/core/focus-utils.ts +89 -0
- package/core/instance.ts +714 -0
- package/core/translations.ts +79 -0
- package/css/pagefind-component-ui.css +1448 -0
- package/npm_dist/cjs/component-ui.cjs +6285 -0
- package/npm_dist/cjs/instance.cjs +2849 -0
- package/npm_dist/mjs/component-ui.mjs +6268 -0
- package/npm_dist/mjs/instance.mjs +2826 -0
- package/package.json +48 -0
- package/types-entry.ts +27 -0
- package/types.ts +126 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { PagefindElement } from "./base-element";
|
|
2
|
+
import { Instance, PagefindComponent } from "../core/instance";
|
|
3
|
+
|
|
4
|
+
interface ModalTrigger extends PagefindComponent {
|
|
5
|
+
buttonEl?: HTMLButtonElement;
|
|
6
|
+
handleModalClose?: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class PagefindModal extends PagefindElement {
|
|
10
|
+
static get observedAttributes(): string[] {
|
|
11
|
+
return ["reset-on-close"];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
dialogEl: HTMLDialogElement | null = null;
|
|
15
|
+
resetOnClose: boolean = false;
|
|
16
|
+
private _isOpen: boolean = false;
|
|
17
|
+
private _closeHandler: (() => void) | null = null;
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
super();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
init(): void {
|
|
24
|
+
if (this.hasAttribute("reset-on-close")) {
|
|
25
|
+
this.resetOnClose = this.getAttribute("reset-on-close") !== "false";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this.render();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
render(): void {
|
|
32
|
+
const hasChildren = this.children.length > 0;
|
|
33
|
+
const children = hasChildren ? Array.from(this.children) : null;
|
|
34
|
+
|
|
35
|
+
this.innerHTML = "";
|
|
36
|
+
|
|
37
|
+
const dialogId = this.id || this.instance!.generateId("pagefind-modal");
|
|
38
|
+
const searchLabel = this.instance?.translate("keyboard_search") || "search";
|
|
39
|
+
|
|
40
|
+
if (this.instance?.direction === "rtl") {
|
|
41
|
+
this.setAttribute("dir", "rtl");
|
|
42
|
+
} else {
|
|
43
|
+
this.removeAttribute("dir");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.dialogEl = document.createElement("dialog");
|
|
47
|
+
this.dialogEl.className = "pf-modal";
|
|
48
|
+
this.dialogEl.id = dialogId;
|
|
49
|
+
this.dialogEl.setAttribute("aria-modal", "true");
|
|
50
|
+
this.dialogEl.setAttribute("aria-label", searchLabel);
|
|
51
|
+
|
|
52
|
+
if (hasChildren && children) {
|
|
53
|
+
// User provided structure - move children into dialog as-is
|
|
54
|
+
children.forEach((child) => this.dialogEl!.appendChild(child));
|
|
55
|
+
} else {
|
|
56
|
+
// Generate default structure, inheriting instance attribute if present
|
|
57
|
+
const inst = this.getAttribute("instance");
|
|
58
|
+
|
|
59
|
+
const header = document.createElement("pagefind-modal-header");
|
|
60
|
+
const input = document.createElement("pagefind-input");
|
|
61
|
+
if (inst) input.setAttribute("instance", inst);
|
|
62
|
+
header.appendChild(input);
|
|
63
|
+
|
|
64
|
+
const body = document.createElement("pagefind-modal-body");
|
|
65
|
+
const summary = document.createElement("pagefind-summary");
|
|
66
|
+
const results = document.createElement("pagefind-results");
|
|
67
|
+
if (inst) {
|
|
68
|
+
summary.setAttribute("instance", inst);
|
|
69
|
+
results.setAttribute("instance", inst);
|
|
70
|
+
}
|
|
71
|
+
body.append(summary, results);
|
|
72
|
+
|
|
73
|
+
const footer = document.createElement("pagefind-modal-footer");
|
|
74
|
+
const hints = document.createElement("pagefind-keyboard-hints");
|
|
75
|
+
if (inst) hints.setAttribute("instance", inst);
|
|
76
|
+
footer.appendChild(hints);
|
|
77
|
+
|
|
78
|
+
this.dialogEl.append(header, body, footer);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.appendChild(this.dialogEl);
|
|
82
|
+
this.setupEventHandlers();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private setupEventHandlers(): void {
|
|
86
|
+
if (!this.dialogEl) return;
|
|
87
|
+
|
|
88
|
+
this._closeHandler = () => {
|
|
89
|
+
this._isOpen = false;
|
|
90
|
+
this.handleClose();
|
|
91
|
+
};
|
|
92
|
+
this.dialogEl.addEventListener("close", this._closeHandler);
|
|
93
|
+
|
|
94
|
+
this.dialogEl.addEventListener(
|
|
95
|
+
"keydown",
|
|
96
|
+
(e) => {
|
|
97
|
+
if (e.key === "Escape") {
|
|
98
|
+
e.stopPropagation();
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
true,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
this.dialogEl.addEventListener("click", (e) => {
|
|
105
|
+
if (e.target === this.dialogEl) {
|
|
106
|
+
this.close();
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
open(): void {
|
|
112
|
+
if (this._isOpen || !this.dialogEl) return;
|
|
113
|
+
|
|
114
|
+
this._isOpen = true;
|
|
115
|
+
this.dialogEl.showModal();
|
|
116
|
+
|
|
117
|
+
const closeText = this.instance?.translate("keyboard_close") || "close";
|
|
118
|
+
this.instance?.registerShortcut(
|
|
119
|
+
{ label: "esc", description: closeText },
|
|
120
|
+
this,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
requestAnimationFrame(() => {
|
|
124
|
+
const input = this.querySelector(
|
|
125
|
+
"pagefind-input",
|
|
126
|
+
) as PagefindComponent | null;
|
|
127
|
+
if (input && typeof input.focus === "function") {
|
|
128
|
+
input.focus();
|
|
129
|
+
} else {
|
|
130
|
+
const inputEl = this.querySelector("input");
|
|
131
|
+
if (inputEl) {
|
|
132
|
+
inputEl.focus();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const triggers = (this.instance?.getUtilities("modal-trigger") ||
|
|
138
|
+
[]) as ModalTrigger[];
|
|
139
|
+
triggers.forEach((t) => t.buttonEl?.setAttribute("aria-expanded", "true"));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
close(): void {
|
|
143
|
+
if (!this._isOpen || !this.dialogEl) return;
|
|
144
|
+
|
|
145
|
+
this.dialogEl.close();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private handleClose(): void {
|
|
149
|
+
this.instance?.deregisterAllShortcuts(this);
|
|
150
|
+
|
|
151
|
+
if (this.resetOnClose && this.instance) {
|
|
152
|
+
this.instance.triggerSearch("");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const triggers = (this.instance?.getUtilities("modal-trigger") ||
|
|
156
|
+
[]) as ModalTrigger[];
|
|
157
|
+
const trigger = triggers[0];
|
|
158
|
+
if (trigger && typeof trigger.handleModalClose === "function") {
|
|
159
|
+
trigger.handleModalClose();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get isOpen(): boolean {
|
|
164
|
+
return this._isOpen;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
register(instance: Instance): void {
|
|
168
|
+
instance.registerUtility(this, "modal");
|
|
169
|
+
|
|
170
|
+
instance.on(
|
|
171
|
+
"translations",
|
|
172
|
+
() => {
|
|
173
|
+
const wasOpen = this._isOpen;
|
|
174
|
+
this.render();
|
|
175
|
+
if (wasOpen) {
|
|
176
|
+
this.open();
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
this,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
reconcileAria(): void {
|
|
184
|
+
const triggers = (this.instance?.getUtilities("modal-trigger") ||
|
|
185
|
+
[]) as ModalTrigger[];
|
|
186
|
+
triggers.forEach((t) => {
|
|
187
|
+
if (t.buttonEl && this.dialogEl?.id) {
|
|
188
|
+
t.buttonEl.setAttribute("aria-controls", this.dialogEl.id);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
cleanup(): void {
|
|
194
|
+
if (this.dialogEl && this._closeHandler) {
|
|
195
|
+
this.dialogEl.removeEventListener("close", this._closeHandler);
|
|
196
|
+
}
|
|
197
|
+
this.instance?.deregisterAllShortcuts(this);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
update(): void {
|
|
201
|
+
if (this.hasAttribute("reset-on-close")) {
|
|
202
|
+
this.resetOnClose = this.getAttribute("reset-on-close") !== "false";
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!customElements.get("pagefind-modal")) {
|
|
208
|
+
customElements.define("pagefind-modal", PagefindModal);
|
|
209
|
+
}
|