@penn-libraries/web 1.2.0-dev.2 → 1.2.0

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.
Files changed (46) hide show
  1. package/dist/cjs/loader.cjs.js +1 -1
  2. package/dist/cjs/pennlibs-autocomplete.cjs.entry.js +281 -0
  3. package/dist/cjs/pennlibs-autocomplete.entry.cjs.js.map +1 -0
  4. package/dist/cjs/pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.cjs.js.map +1 -0
  5. package/dist/cjs/{pennlibs-autocomplete_4.cjs.entry.js → pennlibs-feedback_3.cjs.entry.js} +25 -297
  6. package/dist/cjs/web.cjs.js +1 -1
  7. package/dist/collection/components/pennlibs-feedback/pennlibs-feedback.css +21 -0
  8. package/dist/collection/components/pennlibs-feedback/pennlibs-feedback.js +25 -21
  9. package/dist/collection/components/pennlibs-feedback/pennlibs-feedback.js.map +1 -1
  10. package/dist/components/pennlibs-feedback.js +26 -22
  11. package/dist/components/pennlibs-feedback.js.map +1 -1
  12. package/dist/docs.json +1 -1
  13. package/dist/esm/{index-CNQ9-Wwh.js → index-mkVX9lU0.js} +3 -3
  14. package/dist/esm/{index-CNQ9-Wwh.js.map → index-mkVX9lU0.js.map} +1 -1
  15. package/dist/esm/index.js +1 -1
  16. package/dist/esm/loader.js +3 -3
  17. package/dist/esm/pennlibs-autocomplete.entry.js +279 -0
  18. package/dist/esm/pennlibs-autocomplete.entry.js.map +1 -0
  19. package/dist/esm/pennlibs-banner.entry.js +1 -1
  20. package/dist/esm/pennlibs-chat.entry.js +1 -1
  21. package/dist/esm/pennlibs-fallback-img.entry.js +1 -1
  22. package/dist/esm/pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.js.map +1 -0
  23. package/dist/esm/{pennlibs-autocomplete_4.entry.js → pennlibs-feedback_3.entry.js} +27 -298
  24. package/dist/esm/pennlibs-hero.entry.js +1 -1
  25. package/dist/esm/pennlibs-iiif-img.entry.js +1 -1
  26. package/dist/esm/web.js +3 -3
  27. package/dist/types/components/pennlibs-feedback/pennlibs-feedback.d.ts +1 -1
  28. package/dist/web/index.esm.js +1 -1
  29. package/dist/web/{p-a34e1e3e.entry.js → p-0c1113f4.entry.js} +1 -1
  30. package/dist/web/{p-3da26275.entry.js → p-10931f0a.entry.js} +27 -298
  31. package/dist/web/{p-b6e686d1.entry.js → p-154851d1.entry.js} +1 -1
  32. package/dist/web/{p-c96a3643.entry.js → p-190f0293.entry.js} +1 -1
  33. package/dist/web/{p-eb633f4d.entry.js → p-4fb3e5a4.entry.js} +1 -1
  34. package/dist/web/p-7c6382d0.entry.js +279 -0
  35. package/dist/web/{p-662e4125.entry.js → p-e426fb61.entry.js} +1 -1
  36. package/dist/web/{p-CNQ9-Wwh.js → p-mkVX9lU0.js} +3 -3
  37. package/dist/web/{p-CNQ9-Wwh.js.map → p-mkVX9lU0.js.map} +1 -1
  38. package/dist/web/pennlibs-autocomplete.entry.esm.js.map +1 -0
  39. package/dist/web/pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.esm.js.map +1 -0
  40. package/dist/web/web.esm.js +3 -3
  41. package/hydrate/index.js +26 -22
  42. package/hydrate/index.mjs +26 -22
  43. package/package.json +1 -1
  44. package/dist/cjs/pennlibs-autocomplete.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.cjs.js.map +0 -1
  45. package/dist/esm/pennlibs-autocomplete.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.js.map +0 -1
  46. package/dist/web/pennlibs-autocomplete.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.esm.js.map +0 -1
@@ -2,288 +2,11 @@
2
2
 
3
3
  var index = require('./index-Ag6DtE87.js');
4
4
 
5
- const pennlibsAutocompleteCss = ":host {\n display: block;\n width: 100%;\n border-radius: 1.25rem;\n padding: 0;\n border-top: 0;\n position: relative;\n}\n\n[role=listbox] {\n position: absolute;\n margin-top: var(--pl-space-xs);\n background: var(--pl-color-bg-default);\n border-radius: 1.25rem;\n box-shadow: rgba(140, 149, 159, 0.3) 0px 8px 24px 0px;\n width: 100%;\n overflow: hidden;\n z-index: 1;\n\n display: flex;\n flex-direction: column;\n}\n\np {\n margin: 0;\n font-size: var(--pl-font-size-s);\n color: var(--pl-color-fg-subtle);\n padding: var(--pl-space-xs) calc(var(--pl-space-m) + var(--pl-space-2xs));\n font-size: var(--pl-font-size-s);\n order: 2;\n font-weight: 500;\n background: var(--pl-color-bg-subtle);\n border-radius: 0 0 1.25rem 1.25rem;\n\n display: flex;\n gap: var(--pl-space-s) var(--pl-space-l);\n flex-wrap: wrap;\n}\n\nol {\n list-style: none;\n margin: 0;\n padding: var(--pl-space-xs) 0;\n order: 1;\n}\n\n[role=option] {\n color: var(--pl-color-fg-default);\n padding: var(--pl-space-s) calc(var(--pl-space-m) + var(--pl-space-2xs));\n text-decoration: none;\n font-weight: 700; \n\n &:hover {\n cursor: pointer;\n }\n\n &:hover,\n &:focus {\n text-decoration-thickness: 2px;\n text-underline-offset: 2px;\n text-decoration: underline;\n }\n}\n\n[aria-selected=true] {\n text-decoration-thickness: 2px;\n text-underline-offset: 2px;\n text-decoration: underline;\n}\n\nmark,\nb {\n background: none;\n font-weight: 400;\n}\n\n.suggestion--border {\n border-bottom: solid 1px rgb(from var(--pl-color-fg-default) r g b / 0.2);\n padding-bottom: calc(var(--pl-space-2xs) + var(--pl-space-s));\n margin-bottom: var(--pl-space-2xs);\n}";
5
+ const pennlibsFeedbackCss = ":host{display:block;background:#eeeff4;font-family:var(--pl-font-family);font-size:var(--pl-font-size);color:var(--pl-color-fg-default);line-height:1.4;box-sizing:border-box;padding:0.75em 0}@media print{:host{display:none}}*,*::before,*::after{box-sizing:border-box}.viewport-margins{max-width:var(--pl-viewport-margins-max-width);margin:0 auto;padding:0 var(--pl-viewport-margins-gutter, 1em)}.feedback__content{display:flex;justify-content:space-between;gap:var(--pl-space-s);flex-wrap:wrap}.feedback__inline-question{display:flex;flex-wrap:wrap;gap:0.5em;align-items:center}h2{display:inline-block;margin:0;font-family:var(--pl-font-sans-serif);font-size:1em;font-weight:400}p{margin:0;padding:calc(0.5em + 1px) 0}strong{font-weight:600}a{color:var(--pl-color-fg-accent);text-decoration:underline;text-underline-offset:var(--pl-link-text-underline-offset);text-decoration-thickness:var(--pl-link-hover-text-decoration-thickness)}button{all:unset;display:flex;align-items:center;gap:0.5em;padding:0.25em 1.25em;font-family:var(--pl-font-family);font-weight:500;line-height:1.4;color:var(--pl-color-fg-default);background:var(--pl-color-bg-default);border:solid 1px var(--pl-color-fg-subtle);border-radius:1em;box-sizing:border-box}button:hover{cursor:pointer}button:hover span{text-decoration:underline}button.selected{background:var(--pl-color-bg-accent-emphasis);color:var(--pl-color-fg-on-emphasis);border-color:var(--pl-color-bg-accent-emphasis)}.feedback__inline-question+.feedback__share-feedback,p{animation:none}.feedback__content>p{animation:fadeIn 300ms ease-in}@keyframes fadeIn{from{opacity:0}to{opacity:1}}*:focus{outline:0;box-shadow:0 0 0 2px var(--pl-color-bg-attention),\n 0 0 0 3px var(--pl-color-bg-emphasis)}p:focus{outline:none;box-shadow:0 0 0 2px var(--pl-color-bg-attention), 0 0 0 3px var(--pl-color-bg-emphasis)}@media (max-width: 26em){h2{display:block;width:100%;text-align:center}}.help-us{display:flex;flex-direction:column;gap:0.5em}.visually-hidden{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}.visually-hidden:focus,.visually-hidden:active{clip:auto;clip-path:none;height:auto;overflow:visible;position:static;white-space:normal;width:auto}";
6
6
 
7
- const Autocomplete = class {
8
- constructor(hostRef) {
9
- index.registerInstance(this, hostRef);
10
- this.activated = index.createEvent(this, "pl:activated");
11
- this.showSuggestions = false;
12
- this.currentIndex = -1;
13
- this.originalValue = '';
14
- this.options = [];
15
- this.keyHandlerMap = {
16
- 'Escape': () => this.handleEscape(),
17
- 'ArrowDown': () => this.handleArrowDown(),
18
- 'ArrowUp': () => this.handleArrowUp(),
19
- 'Enter': () => this.handleEnter()
20
- };
21
- this.handleOptionClick = (index) => {
22
- this.currentIndex = index;
23
- this.selectCurrent();
24
- };
25
- }
26
- componentWillLoad() {
27
- this.options = this.parseOptionsFromDOM();
28
- }
29
- componentDidLoad() {
30
- this.setupInput();
31
- this.setupMutationObserver();
32
- }
33
- disconnectedCallback() {
34
- if (this.blurTimeout)
35
- clearTimeout(this.blurTimeout);
36
- if (this.mutationObserver)
37
- this.mutationObserver.disconnect();
38
- }
39
- setupMutationObserver() {
40
- this.mutationObserver = new MutationObserver(() => {
41
- this.options = this.parseOptionsFromDOM();
42
- });
43
- this.mutationObserver.observe(this.el, {
44
- childList: true,
45
- subtree: true,
46
- characterData: true
47
- });
48
- }
49
- parseOptionsFromDOM() {
50
- const listbox = this.findListbox();
51
- if (!listbox)
52
- return [];
53
- const elements = Array.from(listbox.querySelectorAll('[role="option"]'));
54
- return elements.map((el, i) => this.createOption(el, i));
55
- }
56
- findListbox() {
57
- return Array.from(this.el.children).find(child => child.matches('ol[role="listbox"]'));
58
- }
59
- createOption(element, index) {
60
- var _a;
61
- return {
62
- id: element.id || `option-${index}`,
63
- html: element.innerHTML,
64
- value: element.getAttribute('data-pl-value') || ((_a = element.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || ''
65
- };
66
- }
67
- getInput() {
68
- var _a;
69
- if (!this.for)
70
- return null;
71
- const slot = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('slot[name="start"]');
72
- if (!slot)
73
- return null;
74
- const assignedElements = slot.assignedElements();
75
- if (assignedElements.length === 0)
76
- return null;
77
- const input = assignedElements[0].querySelector(`input#${this.for}`);
78
- return input || null;
79
- }
80
- isInputFocused() {
81
- return this.getInput() === document.activeElement;
82
- }
83
- isTrackedInput(input) {
84
- return (input === null || input === void 0 ? void 0 : input.getAttribute('data-pl-autocomplete-connected')) === 'true' &&
85
- this.el.contains(input);
86
- }
87
- setupInput() {
88
- if (!this.for) {
89
- console.warn('<pennlibs-autocomplete> Missing "for" attribute. Please add for="input-id" to specify which input to attach to.');
90
- return;
91
- }
92
- const input = this.getInput();
93
- if (!input) {
94
- console.warn(`<pennlibs-autocomplete> No input element found with id="${this.for}". Ensure an input with this id exists.`);
95
- return;
96
- }
97
- this.setComboboxAttributes(input);
98
- input.setAttribute('data-pl-autocomplete-connected', 'true');
99
- }
100
- setComboboxAttributes(input) {
101
- input.setAttribute('role', 'combobox');
102
- input.setAttribute('aria-autocomplete', 'list');
103
- input.setAttribute('aria-expanded', 'false');
104
- input.setAttribute('aria-controls', 'listbox');
105
- }
106
- handleSlotChange() {
107
- this.options = this.parseOptionsFromDOM();
108
- this.setupInput();
109
- }
110
- handleInputEvent(event) {
111
- const input = event.target;
112
- if (this.isTrackedInput(input)) {
113
- this.currentIndex = -1;
114
- this.openSuggestions();
115
- }
116
- }
117
- handleFocusEvent(event) {
118
- const input = event.target;
119
- if (this.isTrackedInput(input))
120
- this.showSuggestionsPanel();
121
- }
122
- handleBlurEvent(event) {
123
- const input = event.target;
124
- if (this.isTrackedInput(input))
125
- this.deferCloseSuggestions();
126
- }
127
- handleFocusOut(event) {
128
- if (this.isFocusLeavingComponent(event))
129
- this.closeSuggestions();
130
- }
131
- handleKeyDown(event) {
132
- if (!this.isInputFocused())
133
- return;
134
- if (event.metaKey || event.ctrlKey)
135
- return; // Ignore keyboard shortcuts
136
- const handler = this.keyHandlerMap[event.key];
137
- if (handler) {
138
- // Don't prevent default for Enter - allow form submission
139
- if (event.key !== 'Enter') {
140
- event.preventDefault();
141
- }
142
- handler();
143
- }
144
- }
145
- handleEscape() {
146
- this.reset();
147
- this.syncAll();
148
- }
149
- handleArrowDown() {
150
- if (!this.showSuggestions)
151
- return;
152
- this.moveNext();
153
- this.syncAll();
154
- }
155
- handleArrowUp() {
156
- if (!this.showSuggestions)
157
- return;
158
- this.movePrevious();
159
- this.syncAll();
160
- }
161
- handleEnter() {
162
- if (this.canSelect())
163
- this.selectCurrent();
164
- }
165
- // Navigation
166
- moveNext() {
167
- const lastIndex = this.options.length - 1;
168
- this.currentIndex = this.currentIndex < lastIndex ? this.currentIndex + 1 : -1;
169
- }
170
- movePrevious() {
171
- const lastIndex = this.options.length - 1;
172
- this.currentIndex = this.currentIndex > -1 ? this.currentIndex - 1 : lastIndex;
173
- }
174
- canSelect() {
175
- return this.showSuggestions && this.currentIndex >= 0;
176
- }
177
- selectCurrent() {
178
- if (this.currentIndex < 0)
179
- return;
180
- const selectedOption = this.options[this.currentIndex];
181
- this.applySelection(selectedOption);
182
- this.emitActivation(selectedOption);
183
- this.closeSuggestions();
184
- }
185
- applySelection(option) {
186
- const input = this.getInput();
187
- if (!input)
188
- return;
189
- input.value = option.value;
190
- this.originalValue = option.value;
191
- }
192
- emitActivation(option) {
193
- this.activated.emit({
194
- index: this.currentIndex,
195
- value: option.value
196
- });
197
- }
198
- openSuggestions() {
199
- if (!this.isInputFocused())
200
- return;
201
- const input = this.getInput();
202
- if (input)
203
- this.originalValue = input.value;
204
- this.showSuggestionsPanel();
205
- }
206
- showSuggestionsPanel() {
207
- this.showSuggestions = true;
208
- this.syncAriaAttributes();
209
- }
210
- closeSuggestions() {
211
- this.reset();
212
- this.syncAll();
213
- }
214
- deferCloseSuggestions() {
215
- if (this.blurTimeout)
216
- clearTimeout(this.blurTimeout);
217
- this.blurTimeout = setTimeout(() => {
218
- if (this.isFocusOutsideComponent())
219
- this.closeSuggestions();
220
- }, 150);
221
- }
222
- reset() {
223
- this.showSuggestions = false;
224
- this.currentIndex = -1;
225
- }
226
- isFocusLeavingComponent(event) {
227
- var _a;
228
- const target = event.relatedTarget;
229
- return !target ||
230
- (!this.el.contains(target) && !((_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.contains(target)));
231
- }
232
- isFocusOutsideComponent() {
233
- var _a;
234
- const active = document.activeElement;
235
- return !((_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.contains(active)) && active !== this.getInput();
236
- }
237
- syncAriaAttributes() {
238
- const input = this.getInput();
239
- if (!input)
240
- return;
241
- this.updateAriaExpanded(input);
242
- this.updateAriaActiveDescendant(input);
243
- }
244
- syncInputValueToSelection() {
245
- const input = this.getInput();
246
- if (!input)
247
- return;
248
- input.value = this.currentIndex >= 0 && this.currentIndex < this.options.length
249
- ? this.options[this.currentIndex].value
250
- : this.originalValue;
251
- }
252
- syncAll() {
253
- this.syncAriaAttributes();
254
- this.syncInputValueToSelection();
255
- }
256
- updateAriaExpanded(input) {
257
- input.setAttribute('aria-expanded', this.showSuggestions.toString());
258
- }
259
- updateAriaActiveDescendant(input) {
260
- const hasSelection = this.showSuggestions &&
261
- this.currentIndex >= 0 &&
262
- this.currentIndex < this.options.length;
263
- if (hasSelection) {
264
- input.setAttribute('aria-activedescendant', this.options[this.currentIndex].id);
265
- }
266
- else {
267
- input.removeAttribute('aria-activedescendant');
268
- }
269
- }
270
- shouldShowListbox() {
271
- return this.showSuggestions && this.options.length > 0 && this.isInputFocused();
272
- }
273
- render() {
274
- return (index.h("div", { key: 'ee4ead1b5bec44a561901a29d3d5e119da0d09f8' }, index.h("slot", { key: '7d7128ab7968544ab25125d30a648136f8d8a566', name: "start" }), this.shouldShowListbox() && (index.h("ol", { key: '95d45d524847f83b646853a8d919bc88f471f150', role: "listbox", id: "listbox" }, this.options.map((option, index$1) => (index.h("li", { role: "option", id: option.id, "aria-selected": this.currentIndex === index$1 ? 'true' : 'false', tabindex: "-1", onClick: () => this.handleOptionClick(index$1), innerHTML: option.html })))))));
275
- }
276
- get el() { return index.getElement(this); }
277
- };
278
- Autocomplete.style = pennlibsAutocompleteCss;
279
-
280
- const pennlibsFeedbackCss = ":host{display:block;background:#eeeff4;font-family:var(--pl-font-family);font-size:var(--pl-font-size);color:var(--pl-color-fg-default);line-height:1.4;box-sizing:border-box;padding:0.75em 0}@media print{:host{display:none}}*,*::before,*::after{box-sizing:border-box}.viewport-margins{max-width:var(--pl-viewport-margins-max-width);margin:0 auto;padding:0 var(--pl-viewport-margins-gutter, 1em)}.feedback__content{display:flex;justify-content:space-between;gap:var(--pl-space-s);flex-wrap:wrap}.feedback__inline-question{display:flex;flex-wrap:wrap;gap:0.5em;align-items:center}h2{display:inline-block;margin:0;font-family:var(--pl-font-sans-serif);font-size:1em;font-weight:400}p{margin:0;padding:calc(0.5em + 1px) 0}strong{font-weight:600}a{color:var(--pl-color-fg-accent);text-decoration:underline;text-underline-offset:var(--pl-link-text-underline-offset);text-decoration-thickness:var(--pl-link-hover-text-decoration-thickness)}button{all:unset;display:flex;align-items:center;gap:0.5em;padding:0.25em 1.25em;font-family:var(--pl-font-family);font-weight:500;line-height:1.4;color:var(--pl-color-fg-default);background:var(--pl-color-bg-default);border:solid 1px var(--pl-color-fg-subtle);border-radius:1em;box-sizing:border-box}button:hover{cursor:pointer}button:hover span{text-decoration:underline}*:focus{outline:0;box-shadow:0 0 0 2px var(--pl-color-bg-attention),\n 0 0 0 3px var(--pl-color-bg-emphasis)}p:focus{outline:none;box-shadow:0 0 0 2px var(--pl-color-bg-attention), 0 0 0 3px var(--pl-color-bg-emphasis)}@media (max-width: 26em){h2{display:block;width:100%;text-align:center}}.help-us{display:flex;flex-direction:column;gap:0.5em}.visually-hidden{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}.visually-hidden:focus,.visually-hidden:active{clip:auto;clip-path:none;height:auto;overflow:visible;position:static;white-space:normal;width:auto}";
281
-
282
- const FEEDBACK_STORAGE_KEY = 'pennlibs-feedback-answer';
283
- const FEEDBACK_TIMESTAMP_KEY = 'pennlibs-feedback-timestamp';
7
+ const FEEDBACK_STORAGE_KEY = `pennlibs-feedback-${window.location.pathname}`;
284
8
  const FEEDBACK_TTL = 5 * 60 * 1000; // 5 minutes
285
- const FEEDBACK_QUESTION = 'Is this page useful?';
286
- const FEEDBACK_ERROR_STORAGE_KEY = 'pennlibs-feedback-error';
9
+ const FEEDBACK_QUESTION = 'Did you find what you need?';
287
10
  const PennlibsFeedback = class {
288
11
  constructor(hostRef) {
289
12
  index.registerInstance(this, hostRef);
@@ -292,27 +15,30 @@ const PennlibsFeedback = class {
292
15
  * When true, hides the yes/no question and only shows the "share your feedback" link.
293
16
  */
294
17
  this.hideQuestion = false;
295
- this.error = false;
296
18
  this.answer = undefined;
19
+ this.selected = undefined;
297
20
  }
298
21
  componentWillLoad() {
299
- const timestamp = sessionStorage.getItem(FEEDBACK_TIMESTAMP_KEY);
300
- if (timestamp && Date.now() - Number(timestamp) > FEEDBACK_TTL) {
301
- sessionStorage.removeItem(FEEDBACK_STORAGE_KEY);
302
- sessionStorage.removeItem(FEEDBACK_TIMESTAMP_KEY);
303
- }
304
- if (sessionStorage.getItem(FEEDBACK_STORAGE_KEY)) {
305
- this.answer = sessionStorage.getItem(FEEDBACK_STORAGE_KEY);
306
- }
307
- if (sessionStorage.getItem(FEEDBACK_ERROR_STORAGE_KEY)) {
308
- this.error = sessionStorage.getItem(FEEDBACK_ERROR_STORAGE_KEY) === 'true';
22
+ const stored = sessionStorage.getItem(FEEDBACK_STORAGE_KEY);
23
+ if (stored) {
24
+ try {
25
+ const { answer, timestamp } = JSON.parse(stored);
26
+ if (Date.now() - timestamp > FEEDBACK_TTL) {
27
+ sessionStorage.removeItem(FEEDBACK_STORAGE_KEY);
28
+ }
29
+ else {
30
+ this.answer = answer;
31
+ }
32
+ }
33
+ catch (_a) {
34
+ sessionStorage.removeItem(FEEDBACK_STORAGE_KEY);
35
+ }
309
36
  }
310
37
  }
311
38
  handleFeedback(answer) {
312
39
  var _a;
313
- this.answer = answer;
314
- sessionStorage.setItem(FEEDBACK_STORAGE_KEY, answer);
315
- sessionStorage.setItem(FEEDBACK_TIMESTAMP_KEY, String(Date.now()));
40
+ this.selected = answer;
41
+ sessionStorage.setItem(FEEDBACK_STORAGE_KEY, JSON.stringify({ answer, timestamp: Date.now() }));
316
42
  this.feedbackEvent.emit({
317
43
  question: FEEDBACK_QUESTION,
318
44
  answer: answer,
@@ -320,9 +46,12 @@ const PennlibsFeedback = class {
320
46
  pageHeading: ((_a = document.querySelector('h1')) === null || _a === void 0 ? void 0 : _a.textContent) || undefined,
321
47
  pageTitle: document.title || undefined
322
48
  });
49
+ setTimeout(() => {
50
+ this.answer = answer;
51
+ }, 600);
323
52
  }
324
53
  render() {
325
- return (index.h("div", { key: '04053c96e964ee9f240ca1a77d7944ae0f53248d', class: "feedback__container" }, index.h("div", { key: 'f5f524b449d3473c1ecc2d454787034c54a811a0', class: "viewport-margins" }, index.h("div", { key: 'd0c832edc9361dac49d9cbfc143332fea04434ea', class: "feedback__content" }, !this.hideQuestion && (this.answer === 'yes' ? (index.h("p", null, "Thank you for letting us know.")) : this.answer === 'no' ? (index.h("p", null, "Thank you for letting us know. ", index.h("a", { href: "https://www.library.upenn.edu/contact-us" }, "Contact us"), " if you need help.")) : (index.h("div", { class: "feedback__inline-question" }, index.h("h2", null, "Did you find what you need?"), index.h("button", { onClick: () => this.handleFeedback('yes') }, index.h("span", null, "Yes"), index.h("span", { class: "visually-hidden" }, ", I found what I needed.")), index.h("button", { onClick: () => this.handleFeedback('no') }, index.h("span", null, "No"), index.h("span", { class: "visually-hidden" }, ", I did not find what I needed."))))), index.h("div", { key: '9d4de668c9e3696d8983c48fcc20ea89b2163ea1', class: "feedback__share-feedback" }, index.h("p", { key: '1377153ebba81c48b0a70756b190503b9cd33c61' }, "To help us improve, ", index.h("a", { key: '26f3f05b0af848ff58d959190a380d5b64db254a', href: "http://library.upenn.edu/feedback" }, "share your feedback"), "."))))));
54
+ return (index.h("div", { key: 'e470cb9ce96fecddda37c8a557c6670b581d8414', class: "feedback__container" }, index.h("div", { key: '0f3729d63102379f7219fd9a65c370f05c3f271c', class: "viewport-margins" }, index.h("div", { key: '3eaa8419d9b5fed5d7cf33de60ab93dd911afa34', class: "feedback__content" }, !this.hideQuestion && (this.answer === 'yes' ? (index.h("p", null, "Thank you for letting us know.")) : this.answer === 'no' ? (index.h("p", null, "We'd like to help. ", index.h("a", { href: "https://www.library.upenn.edu/contact-us" }, "Contact us"), " and let us know what you're looking for.")) : (index.h("div", { class: "feedback__inline-question" }, index.h("h2", null, "Did you find what you need?"), index.h("button", { class: { 'selected': this.selected === 'yes' }, disabled: !!this.selected, onClick: () => this.handleFeedback('yes') }, index.h("span", null, "Yes"), index.h("span", { class: "visually-hidden" }, ", I found what I needed.")), index.h("button", { class: { 'selected': this.selected === 'no' }, disabled: !!this.selected, onClick: () => this.handleFeedback('no') }, index.h("span", null, "No"), index.h("span", { class: "visually-hidden" }, ", I did not find what I needed."))))), index.h("div", { key: '50da9c1f23d14feeb6a2c408b0461a439331fcd9', class: "feedback__share-feedback" }, index.h("p", { key: '3580e25ecd7b463bb46103a6aee54a710e88c0ff' }, "To help us improve, ", index.h("a", { key: '276e226f4487ce94b9de30ae40f964e63f0d045b', href: `https://upenn.co1.qualtrics.com/jfe/form/SV_3xVrP7EKWS92zfE?reporting_from_url=${encodeURIComponent(window.location.href)}` }, "share your feedback"), "."))))));
326
55
  }
327
56
  };
328
57
  PennlibsFeedback.style = pennlibsFeedbackCss;
@@ -430,8 +159,7 @@ const Header = class {
430
159
  };
431
160
  Header.style = pennlibsHeaderCss;
432
161
 
433
- exports.pennlibs_autocomplete = Autocomplete;
434
162
  exports.pennlibs_feedback = PennlibsFeedback;
435
163
  exports.pennlibs_footer = Footer;
436
164
  exports.pennlibs_header = Header;
437
- //# sourceMappingURL=pennlibs-autocomplete.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.cjs.js.map
165
+ //# sourceMappingURL=pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.cjs.js.map
@@ -18,7 +18,7 @@ var patchBrowser = () => {
18
18
 
19
19
  patchBrowser().then(async (options) => {
20
20
  await index.globalScripts();
21
- return index.bootstrapLazy([["pennlibs-iiif-img.cjs",[[257,"pennlibs-iiif-img",{"uuid":[1],"alt":[1],"region":[513],"rotation":[1],"quality":[1],"loading":[1],"showFallback":[4,"show-fallback"],"isLoaded":[32],"hasError":[32],"imageDimensions":[32],"observedWidth":[32]},null,{"region":["handleRegionChange"]}]]],["pennlibs-autocomplete_4.cjs",[[257,"pennlibs-autocomplete",{"for":[1],"showSuggestions":[32],"currentIndex":[32],"originalValue":[32],"options":[32]},[[0,"slotchange","handleSlotChange"],[16,"input","handleInputEvent"],[16,"focus","handleFocusEvent"],[16,"blur","handleBlurEvent"],[0,"focusout","handleFocusOut"],[4,"keydown","handleKeyDown"]]],[257,"pennlibs-feedback",{"hideQuestion":[4,"hide-question"],"error":[32],"answer":[32]}],[257,"pennlibs-footer",{"showBuiltWith":[4,"show-built-with"],"navigationByChildren":[32]}],[257,"pennlibs-header",{"serviceName":[1,"service-name"],"serviceLede":[1,"service-lede"],"serviceHref":[1,"service-href"],"theme":[1],"isMenuOpen":[32],"navigation":[32]}]]],["pennlibs-banner.cjs",[[257,"pennlibs-banner"]]],["pennlibs-chat.cjs",[[257,"pennlibs-chat",{"href":[32]}]]],["pennlibs-hero.cjs",[[257,"pennlibs-hero",{"heroPictureElement":[32],"heroHeadingElement":[32],"heroParagraphElement":[32],"heroSrc":[32]}]]],["pennlibs-fallback-img.cjs",[[257,"pennlibs-fallback-img"]]]], options);
21
+ return index.bootstrapLazy([["pennlibs-iiif-img.cjs",[[257,"pennlibs-iiif-img",{"uuid":[1],"alt":[1],"region":[513],"rotation":[1],"quality":[1],"loading":[1],"showFallback":[4,"show-fallback"],"isLoaded":[32],"hasError":[32],"imageDimensions":[32],"observedWidth":[32]},null,{"region":["handleRegionChange"]}]]],["pennlibs-feedback_3.cjs",[[257,"pennlibs-feedback",{"hideQuestion":[4,"hide-question"],"answer":[32],"selected":[32]}],[257,"pennlibs-footer",{"showBuiltWith":[4,"show-built-with"],"navigationByChildren":[32]}],[257,"pennlibs-header",{"serviceName":[1,"service-name"],"serviceLede":[1,"service-lede"],"serviceHref":[1,"service-href"],"theme":[1],"isMenuOpen":[32],"navigation":[32]}]]],["pennlibs-autocomplete.cjs",[[257,"pennlibs-autocomplete",{"for":[1],"showSuggestions":[32],"currentIndex":[32],"originalValue":[32],"options":[32]},[[0,"slotchange","handleSlotChange"],[16,"input","handleInputEvent"],[16,"focus","handleFocusEvent"],[16,"blur","handleBlurEvent"],[0,"focusout","handleFocusOut"],[4,"keydown","handleKeyDown"]]]]],["pennlibs-banner.cjs",[[257,"pennlibs-banner"]]],["pennlibs-chat.cjs",[[257,"pennlibs-chat",{"href":[32]}]]],["pennlibs-hero.cjs",[[257,"pennlibs-hero",{"heroPictureElement":[32],"heroHeadingElement":[32],"heroParagraphElement":[32],"heroSrc":[32]}]]],["pennlibs-fallback-img.cjs",[[257,"pennlibs-fallback-img"]]]], options);
22
22
  });
23
23
 
24
24
  exports.setNonce = index.setNonce;
@@ -97,6 +97,27 @@ button:hover span {
97
97
  text-decoration: underline;
98
98
  }
99
99
 
100
+ button.selected {
101
+ background: var(--pl-color-bg-accent-emphasis);
102
+ color: var(--pl-color-fg-on-emphasis);
103
+ border-color: var(--pl-color-bg-accent-emphasis);
104
+ }
105
+
106
+ /* Thank you message fade-in */
107
+ .feedback__inline-question + .feedback__share-feedback,
108
+ p {
109
+ animation: none;
110
+ }
111
+
112
+ .feedback__content > p {
113
+ animation: fadeIn 300ms ease-in;
114
+ }
115
+
116
+ @keyframes fadeIn {
117
+ from { opacity: 0; }
118
+ to { opacity: 1; }
119
+ }
120
+
100
121
  /* Focus States
101
122
  ---------------------------------------- */
102
123
  *:focus {
@@ -1,9 +1,7 @@
1
1
  import { h } from "@stencil/core";
2
- const FEEDBACK_STORAGE_KEY = 'pennlibs-feedback-answer';
3
- const FEEDBACK_TIMESTAMP_KEY = 'pennlibs-feedback-timestamp';
2
+ const FEEDBACK_STORAGE_KEY = `pennlibs-feedback-${window.location.pathname}`;
4
3
  const FEEDBACK_TTL = 5 * 60 * 1000; // 5 minutes
5
- const FEEDBACK_QUESTION = 'Is this page useful?';
6
- const FEEDBACK_ERROR_STORAGE_KEY = 'pennlibs-feedback-error';
4
+ const FEEDBACK_QUESTION = 'Did you find what you need?';
7
5
  /**
8
6
  * A feedback component that asks visitors "Did you find what you need?" and
9
7
  * links to a feedback form. The answer is stored in sessionStorage for 5 minutes
@@ -21,27 +19,30 @@ export class PennlibsFeedback {
21
19
  * When true, hides the yes/no question and only shows the "share your feedback" link.
22
20
  */
23
21
  this.hideQuestion = false;
24
- this.error = false;
25
22
  this.answer = undefined;
23
+ this.selected = undefined;
26
24
  }
27
25
  componentWillLoad() {
28
- const timestamp = sessionStorage.getItem(FEEDBACK_TIMESTAMP_KEY);
29
- if (timestamp && Date.now() - Number(timestamp) > FEEDBACK_TTL) {
30
- sessionStorage.removeItem(FEEDBACK_STORAGE_KEY);
31
- sessionStorage.removeItem(FEEDBACK_TIMESTAMP_KEY);
32
- }
33
- if (sessionStorage.getItem(FEEDBACK_STORAGE_KEY)) {
34
- this.answer = sessionStorage.getItem(FEEDBACK_STORAGE_KEY);
35
- }
36
- if (sessionStorage.getItem(FEEDBACK_ERROR_STORAGE_KEY)) {
37
- this.error = sessionStorage.getItem(FEEDBACK_ERROR_STORAGE_KEY) === 'true';
26
+ const stored = sessionStorage.getItem(FEEDBACK_STORAGE_KEY);
27
+ if (stored) {
28
+ try {
29
+ const { answer, timestamp } = JSON.parse(stored);
30
+ if (Date.now() - timestamp > FEEDBACK_TTL) {
31
+ sessionStorage.removeItem(FEEDBACK_STORAGE_KEY);
32
+ }
33
+ else {
34
+ this.answer = answer;
35
+ }
36
+ }
37
+ catch (_a) {
38
+ sessionStorage.removeItem(FEEDBACK_STORAGE_KEY);
39
+ }
38
40
  }
39
41
  }
40
42
  handleFeedback(answer) {
41
43
  var _a;
42
- this.answer = answer;
43
- sessionStorage.setItem(FEEDBACK_STORAGE_KEY, answer);
44
- sessionStorage.setItem(FEEDBACK_TIMESTAMP_KEY, String(Date.now()));
44
+ this.selected = answer;
45
+ sessionStorage.setItem(FEEDBACK_STORAGE_KEY, JSON.stringify({ answer, timestamp: Date.now() }));
45
46
  this.feedbackEvent.emit({
46
47
  question: FEEDBACK_QUESTION,
47
48
  answer: answer,
@@ -49,9 +50,12 @@ export class PennlibsFeedback {
49
50
  pageHeading: ((_a = document.querySelector('h1')) === null || _a === void 0 ? void 0 : _a.textContent) || undefined,
50
51
  pageTitle: document.title || undefined
51
52
  });
53
+ setTimeout(() => {
54
+ this.answer = answer;
55
+ }, 600);
52
56
  }
53
57
  render() {
54
- return (h("div", { key: '04053c96e964ee9f240ca1a77d7944ae0f53248d', class: "feedback__container" }, h("div", { key: 'f5f524b449d3473c1ecc2d454787034c54a811a0', class: "viewport-margins" }, h("div", { key: 'd0c832edc9361dac49d9cbfc143332fea04434ea', class: "feedback__content" }, !this.hideQuestion && (this.answer === 'yes' ? (h("p", null, "Thank you for letting us know.")) : this.answer === 'no' ? (h("p", null, "Thank you for letting us know. ", h("a", { href: "https://www.library.upenn.edu/contact-us" }, "Contact us"), " if you need help.")) : (h("div", { class: "feedback__inline-question" }, h("h2", null, "Did you find what you need?"), h("button", { onClick: () => this.handleFeedback('yes') }, h("span", null, "Yes"), h("span", { class: "visually-hidden" }, ", I found what I needed.")), h("button", { onClick: () => this.handleFeedback('no') }, h("span", null, "No"), h("span", { class: "visually-hidden" }, ", I did not find what I needed."))))), h("div", { key: '9d4de668c9e3696d8983c48fcc20ea89b2163ea1', class: "feedback__share-feedback" }, h("p", { key: '1377153ebba81c48b0a70756b190503b9cd33c61' }, "To help us improve, ", h("a", { key: '26f3f05b0af848ff58d959190a380d5b64db254a', href: "http://library.upenn.edu/feedback" }, "share your feedback"), "."))))));
58
+ return (h("div", { key: 'e470cb9ce96fecddda37c8a557c6670b581d8414', class: "feedback__container" }, h("div", { key: '0f3729d63102379f7219fd9a65c370f05c3f271c', class: "viewport-margins" }, h("div", { key: '3eaa8419d9b5fed5d7cf33de60ab93dd911afa34', class: "feedback__content" }, !this.hideQuestion && (this.answer === 'yes' ? (h("p", null, "Thank you for letting us know.")) : this.answer === 'no' ? (h("p", null, "We'd like to help. ", h("a", { href: "https://www.library.upenn.edu/contact-us" }, "Contact us"), " and let us know what you're looking for.")) : (h("div", { class: "feedback__inline-question" }, h("h2", null, "Did you find what you need?"), h("button", { class: { 'selected': this.selected === 'yes' }, disabled: !!this.selected, onClick: () => this.handleFeedback('yes') }, h("span", null, "Yes"), h("span", { class: "visually-hidden" }, ", I found what I needed.")), h("button", { class: { 'selected': this.selected === 'no' }, disabled: !!this.selected, onClick: () => this.handleFeedback('no') }, h("span", null, "No"), h("span", { class: "visually-hidden" }, ", I did not find what I needed."))))), h("div", { key: '50da9c1f23d14feeb6a2c408b0461a439331fcd9', class: "feedback__share-feedback" }, h("p", { key: '3580e25ecd7b463bb46103a6aee54a710e88c0ff' }, "To help us improve, ", h("a", { key: '276e226f4487ce94b9de30ae40f964e63f0d045b', href: `https://upenn.co1.qualtrics.com/jfe/form/SV_3xVrP7EKWS92zfE?reporting_from_url=${encodeURIComponent(window.location.href)}` }, "share your feedback"), "."))))));
55
59
  }
56
60
  static get is() { return "pennlibs-feedback"; }
57
61
  static get encapsulation() { return "shadow"; }
@@ -91,8 +95,8 @@ export class PennlibsFeedback {
91
95
  }
92
96
  static get states() {
93
97
  return {
94
- "error": {},
95
- "answer": {}
98
+ "answer": {},
99
+ "selected": {}
96
100
  };
97
101
  }
98
102
  static get events() {
@@ -1 +1 @@
1
- {"version":3,"file":"pennlibs-feedback.js","sourceRoot":"","sources":["../../../src/components/pennlibs-feedback/pennlibs-feedback.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Component, h, Prop, State, Event, EventEmitter } from '@stencil/core';\n\nconst FEEDBACK_STORAGE_KEY = 'pennlibs-feedback-answer';\nconst FEEDBACK_TIMESTAMP_KEY = 'pennlibs-feedback-timestamp';\nconst FEEDBACK_TTL = 5 * 60 * 1000; // 5 minutes\nconst FEEDBACK_QUESTION = 'Is this page useful?';\nconst FEEDBACK_ERROR_STORAGE_KEY = 'pennlibs-feedback-error';\n\n/**\n * A feedback component that asks visitors \"Did you find what you need?\" and\n * links to a feedback form. The answer is stored in sessionStorage for 5 minutes\n * to avoid re-prompting during a browsing session.\n *\n * @example\n * <pennlibs-feedback></pennlibs-feedback>\n *\n * @example\n * <pennlibs-feedback hide-question></pennlibs-feedback>\n */\n@Component({\n tag: 'pennlibs-feedback',\n styleUrl: 'pennlibs-feedback.css',\n shadow: true,\n})\nexport class PennlibsFeedback {\n /**\n * When true, hides the yes/no question and only shows the \"share your feedback\" link.\n */\n @Prop() hideQuestion: boolean = false;\n\n @State() error: boolean = false;\n @State() answer: string | undefined = undefined;\n\n /**\n * Emitted when a visitor answers the feedback question. Includes the question,\n * answer, current URL, and page heading/title for context.\n */\n @Event({\n eventName: 'pennlibs-feedback',\n composed: true,\n cancelable: true,\n bubbles: true,\n }) feedbackEvent!: EventEmitter<{\n question: string;\n answer: string;\n url: string;\n pageHeading?: string;\n pageTitle?: string;\n }>;\n\n componentWillLoad() {\n const timestamp = sessionStorage.getItem(FEEDBACK_TIMESTAMP_KEY);\n if (timestamp && Date.now() - Number(timestamp) > FEEDBACK_TTL) {\n sessionStorage.removeItem(FEEDBACK_STORAGE_KEY);\n sessionStorage.removeItem(FEEDBACK_TIMESTAMP_KEY);\n }\n\n if (sessionStorage.getItem(FEEDBACK_STORAGE_KEY)) {\n this.answer = sessionStorage.getItem(FEEDBACK_STORAGE_KEY) as string;\n }\n\n if (sessionStorage.getItem(FEEDBACK_ERROR_STORAGE_KEY)) {\n this.error = sessionStorage.getItem(FEEDBACK_ERROR_STORAGE_KEY) === 'true';\n }\n }\n\n handleFeedback(answer: string) {\n this.answer = answer;\n sessionStorage.setItem(FEEDBACK_STORAGE_KEY, answer);\n sessionStorage.setItem(FEEDBACK_TIMESTAMP_KEY, String(Date.now()));\n \n this.feedbackEvent.emit({\n question: FEEDBACK_QUESTION,\n answer: answer,\n url: window.location.href,\n pageHeading: document.querySelector('h1')?.textContent || undefined,\n pageTitle: document.title || undefined\n });\n }\n \n render() {\n return (\n <div class=\"feedback__container\">\n <div class=\"viewport-margins\">\n <div class=\"feedback__content\">\n {!this.hideQuestion && (this.answer === 'yes' ? (\n <p>Thank you for letting us know.</p>\n ) : this.answer === 'no' ? (\n <p>Thank you for letting us know. <a href=\"https://www.library.upenn.edu/contact-us\">Contact us</a> if you need help.</p>\n ) : (\n <div class=\"feedback__inline-question\">\n <h2>Did you find what you need?</h2>\n <button onClick={() => this.handleFeedback('yes')}>\n <span>Yes</span><span class=\"visually-hidden\">, I found what I needed.</span>\n </button>\n <button onClick={() => this.handleFeedback('no')}>\n <span>No</span><span class=\"visually-hidden\">, I did not find what I needed.</span>\n </button>\n </div>\n ))}\n <div class=\"feedback__share-feedback\">\n <p>To help us improve, <a href=\"http://library.upenn.edu/feedback\">share your feedback</a>.</p>\n </div>\n </div>\n </div>\n </div>\n )\n }\n}\n\n"]}
1
+ {"version":3,"file":"pennlibs-feedback.js","sourceRoot":"","sources":["../../../src/components/pennlibs-feedback/pennlibs-feedback.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAgB,MAAM,eAAe,CAAC;AAE/E,MAAM,oBAAoB,GAAG,qBAAqB,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;AAC7E,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAChD,MAAM,iBAAiB,GAAG,6BAA6B,CAAC;AAGxD;;;;;;;;;;GAUG;AAMH,MAAM,OAAO,gBAAgB;IAL7B;QAME;;WAEG;QACK,iBAAY,GAAY,KAAK,CAAC;QAE7B,WAAM,GAAuB,SAAS,CAAC;QACvC,aAAQ,GAAuB,SAAS,CAAC;KAgFnD;IA7DC,iBAAiB;QACf,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAC5D,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC;gBACH,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBACjD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,YAAY,EAAE,CAAC;oBAC1C,cAAc,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC;gBAClD,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;gBACvB,CAAC;YACH,CAAC;YAAC,WAAM,CAAC;gBACP,cAAc,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC;IAED,cAAc,CAAC,MAAc;;QAC3B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC;QACvB,cAAc,CAAC,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;QAEhG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;YACtB,QAAQ,EAAE,iBAAiB;YAC3B,MAAM,EAAE,MAAM;YACd,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI;YACzB,WAAW,EAAE,CAAA,MAAA,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,0CAAE,WAAW,KAAI,SAAS;YACnE,SAAS,EAAE,QAAQ,CAAC,KAAK,IAAI,SAAS;SACvC,CAAC,CAAC;QAEH,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACvB,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC;IAED,MAAM;QACJ,OAAO,CACL,4DAAK,KAAK,EAAC,qBAAqB;YAC9B,4DAAK,KAAK,EAAC,kBAAkB;gBAC3B,4DAAK,KAAK,EAAC,mBAAmB;oBAC3B,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,CAC9C,8CAAqC,CACtC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,CACzB;;wBAAsB,SAAG,IAAI,EAAC,0CAA0C,iBAAe;oEAA6C,CACrI,CAAC,CAAC,CAAC,CACF,WAAK,KAAK,EAAC,2BAA2B;wBACpC,4CAAoC;wBACpC,cAAQ,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,QAAQ,KAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;4BAC1H,sBAAgB;4BAAA,YAAM,KAAK,EAAC,iBAAiB,+BAAgC,CACtE;wBACT,cAAQ,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;4BACxH,qBAAe;4BAAA,YAAM,KAAK,EAAC,iBAAiB,sCAAuC,CAC5E,CACL,CACP,CAAC;oBACF,4DAAK,KAAK,EAAC,0BAA0B;wBACnC;;4BAAuB,0DAAG,IAAI,EAAE,kFAAkF,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,0BAAyB;gCAAK,CACtL,CACF,CACF,CACF,CACP,CAAA;IACH,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CACF","sourcesContent":["import { Component, h, Prop, State, Event, EventEmitter } from '@stencil/core';\n\nconst FEEDBACK_STORAGE_KEY = `pennlibs-feedback-${window.location.pathname}`;\nconst FEEDBACK_TTL = 5 * 60 * 1000; // 5 minutes\nconst FEEDBACK_QUESTION = 'Did you find what you need?';\n\n\n/**\n * A feedback component that asks visitors \"Did you find what you need?\" and\n * links to a feedback form. The answer is stored in sessionStorage for 5 minutes\n * to avoid re-prompting during a browsing session.\n *\n * @example\n * <pennlibs-feedback></pennlibs-feedback>\n *\n * @example\n * <pennlibs-feedback hide-question></pennlibs-feedback>\n */\n@Component({\n tag: 'pennlibs-feedback',\n styleUrl: 'pennlibs-feedback.css',\n shadow: true,\n})\nexport class PennlibsFeedback {\n /**\n * When true, hides the yes/no question and only shows the \"share your feedback\" link.\n */\n @Prop() hideQuestion: boolean = false;\n\n @State() answer: string | undefined = undefined;\n @State() selected: string | undefined = undefined;\n\n /**\n * Emitted when a visitor answers the feedback question. Includes the question,\n * answer, current URL, and page heading/title for context.\n */\n @Event({\n eventName: 'pennlibs-feedback',\n composed: true,\n cancelable: true,\n bubbles: true,\n }) feedbackEvent!: EventEmitter<{\n question: string;\n answer: string;\n url: string;\n pageHeading?: string;\n pageTitle?: string;\n }>;\n\n componentWillLoad() {\n const stored = sessionStorage.getItem(FEEDBACK_STORAGE_KEY);\n if (stored) {\n try {\n const { answer, timestamp } = JSON.parse(stored);\n if (Date.now() - timestamp > FEEDBACK_TTL) {\n sessionStorage.removeItem(FEEDBACK_STORAGE_KEY);\n } else {\n this.answer = answer;\n }\n } catch {\n sessionStorage.removeItem(FEEDBACK_STORAGE_KEY);\n }\n }\n }\n\n handleFeedback(answer: string) {\n this.selected = answer;\n sessionStorage.setItem(FEEDBACK_STORAGE_KEY, JSON.stringify({ answer, timestamp: Date.now() }));\n\n this.feedbackEvent.emit({\n question: FEEDBACK_QUESTION,\n answer: answer,\n url: window.location.href,\n pageHeading: document.querySelector('h1')?.textContent || undefined,\n pageTitle: document.title || undefined\n });\n\n setTimeout(() => {\n this.answer = answer;\n }, 600);\n }\n \n render() {\n return (\n <div class=\"feedback__container\">\n <div class=\"viewport-margins\">\n <div class=\"feedback__content\">\n {!this.hideQuestion && (this.answer === 'yes' ? (\n <p>Thank you for letting us know.</p>\n ) : this.answer === 'no' ? (\n <p>We'd like to help. <a href=\"https://www.library.upenn.edu/contact-us\">Contact us</a> and let us know what you're looking for.</p>\n ) : (\n <div class=\"feedback__inline-question\">\n <h2>Did you find what you need?</h2>\n <button class={{ 'selected': this.selected === 'yes' }} disabled={!!this.selected} onClick={() => this.handleFeedback('yes')}>\n <span>Yes</span><span class=\"visually-hidden\">, I found what I needed.</span>\n </button>\n <button class={{ 'selected': this.selected === 'no' }} disabled={!!this.selected} onClick={() => this.handleFeedback('no')}>\n <span>No</span><span class=\"visually-hidden\">, I did not find what I needed.</span>\n </button>\n </div>\n ))}\n <div class=\"feedback__share-feedback\">\n <p>To help us improve, <a href={`https://upenn.co1.qualtrics.com/jfe/form/SV_3xVrP7EKWS92zfE?reporting_from_url=${encodeURIComponent(window.location.href)}`}>share your feedback</a>.</p>\n </div>\n </div>\n </div>\n </div>\n )\n }\n}\n\n"]}