@teipublisher/pb-components 2.6.1 → 2.8.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.
package/src/pb-popover.js CHANGED
@@ -1,391 +1,399 @@
1
- import { LitElement, html, css } from 'lit-element';
2
- import tippy from 'tippy.js';
3
- import { pbMixin } from './pb-mixin.js';
4
- import { getCSSProperty } from "./utils.js";
5
- import * as themes from './pb-popover-themes.js';
6
-
7
- function _injectStylesheet(root, name, cssCode) {
8
- const style = root.querySelector(`#pb-popover-${name}`);
9
- if (!style) {
10
- const container = root.nodeType === Node.DOCUMENT_NODE ? document.head : root;
11
- console.log('Loading tippy styles for theme %s into %o', name, container);
12
- const elem = document.createElement('style');
13
- elem.type = 'text/css';
14
- elem.id = `pb-popover-${name}`;
15
- elem.innerHTML = cssCode;
16
- container.appendChild(elem);
17
- }
18
- }
19
-
20
- export function loadTippyStyles(root, theme) {
21
- _injectStylesheet(root, 'base', themes.base);
22
- if (theme && theme !== 'none') {
23
- const name = themes.camelize(theme);
24
- const themeCSS = themes[name];
25
- if (themeCSS) {
26
- _injectStylesheet(root, name, themeCSS);
27
- }
28
- }
29
- }
30
-
31
- /**
32
- * Show a popover. It may either
33
- *
34
- * 1. be attached to another element on the page which serves as a trigger. For this the
35
- * `for` property must be specified and should contain the ID of the trigger element.
36
- * The whole content of the `pb-popover` element will be shown in the popup.
37
- *
38
- * 2. if no `for` property is specified, the `pb-popover` acts itself as the trigger. The trigger
39
- * text is either taken from a slot named `default` - or the default slot (i.e. the content of the element).
40
- * The content to show in the popup should be supplied in a slot named `alternate`. It is recommended to use an
41
- * HTML `template` to specify the alternate, so it is ignored by the browser:
42
- *
43
- * ```html
44
- * <pb-popover theme="material">
45
- * <span slot="default">ipsum dolor sit amet</span>
46
- * <template slot="alternate">
47
- * <p>At vero eos et <strong>accusam</strong> et justo duo dolores<br>
48
- * et ea rebum.</p>
49
- * </template>
50
- * </pb-popover>
51
- * ```
52
- *
53
- * If you would like popovers to contain nested popovers, choose approach 1 above and use `for`.
54
- *
55
- * If property `persistent` is true, the popover will be shown
56
- * on click. Otherwise display a tooltip on mouseover.
57
- *
58
- * `pb-popover` uses the tippy.js library for the popup.
59
- *
60
- * ## Styling
61
- *
62
- * When showing the popup, the popup content will either be added to the parent shadow DOM - if the `pb-popover`
63
- * is located inside the shadow DOM of another element like `pb-view`; or the document body. This has an
64
- * effect on where CSS styles can be defined: within a `pb-view`, only the styles specified inside the
65
- * CSS attached to the ODD are applied.
66
- *
67
- * @prop {String} for - The id of a trigger element (e.g. a link) to which the popover will
68
- * be attached. If not set, the trigger is the pb-popover itself.
69
- * @prop {"material" | "light" | "translucent" | "light-border"} theme - The tippy theme to use.
70
- * @prop {"auto" | "top" | "bottom" | "left" | "right"} placement - Preferred placement of the popup.
71
- * Default is 'auto'.
72
- * @prop {String} fallbackPlacement - Fallback placement if there is more space on another side.
73
- * Accepts same values as `placement`. Separate by space if more than one.
74
- * @prop {Boolean} persistent - If true, show the 'hand' cursor when hovering over the link; `trigger` will be set to 'click'
75
- * unless defined otherwise; clicking anywhere on the page will close the popup.
76
- * @prop {"click" | "mouseenter" | "focus" | "focusin"} trigger - Defines one or more actions (space separated) which should cause
77
- * the popover to show. If property `persistent` is set, `trigger` will by default be set to `click`.
78
- * @prop {String} popupClass - Additional class names which will be added to the popup element.
79
- * Use this to apply a specific style to certain popovers, but not others.
80
- * @prop {String} remote - An optional URL to asynchronously load the popover's content from. Content will
81
- * be loaded after the popover is displayed. The downloaded HTML content will replace the text set via the alternate slot.
82
- * @prop {Boolean} stopPropagation - If you have nested pb-popover, set this property to
83
- * only show the innermost popover when triggered
84
- *
85
- * @slot default - the content to show for the trigger. If not specified, this will fall back to the unnamed slot.
86
- * @slot alternate - the content to show in the popup
87
- *
88
- * @csspart trigger - the inline element used as trigger
89
- *
90
- * @cssprop [--pb-popover-theme=none] - popup theme to use. One of 'material', 'light', 'translucent' or 'light-border'
91
- * @cssprop [--pb-popover-link-decoration=inherit] - text decoration for the trigger
92
- * @cssprop [--pb-popover-max-height=calc(100vh - 60px)] - limit the maximum height of the popup
93
- * @cssprop --pb-popover-min-height - set the minimum height of the popup
94
- * @cssprop --pb-popover-max-width - limit the max width of the popup
95
- * @cssprop --pb-popover-min-width - set the minimum width of the popup
96
- * @cssprop --pb-popover-color - Color of the popup text
97
- * @cssprop [--pb-popover-placement=auto] - Preferred popup placement, see property `placement`
98
- * @cssprop --pb-popover-fallback-placement - Fallback placements separated by space
99
- * @cssprop --pb-popover-trigger - define the trigger action, same as property `trigger`
100
- * @cssprop --pb-popover-persistent - switch to persistent behaviour, see property `persistent`
101
- */
102
- export class PbPopover extends pbMixin(LitElement) {
103
- static get properties() {
104
- return {
105
- ...super.properties,
106
- for: {
107
- type: String
108
- },
109
- theme: {
110
- type: String
111
- },
112
- placement: {
113
- type: String
114
- },
115
- fallbackPlacement: {
116
- type: String,
117
- attribute: 'fallback-placement'
118
- },
119
- persistent: {
120
- type: Boolean
121
- },
122
- trigger: {
123
- type: String
124
- },
125
- popupClass: {
126
- type: String,
127
- attribute: 'popup-class'
128
- },
129
- remote: {
130
- type: String
131
- },
132
- stopPropagation: {
133
- type: Boolean,
134
- attribute: 'stop-propagation'
135
- }
136
- };
137
- }
138
-
139
- constructor() {
140
- super();
141
- this.persistent = false;
142
- this.trigger = null;
143
- this.for = null;
144
- this.theme = null;
145
- this.placement = null;
146
- this.fallbackPlacement = null;
147
- this.popupClass = null;
148
- this.stopPropagation = false;
149
- this._tippy = null;
150
- this._content = null;
151
- }
152
-
153
- render() {
154
- if (this.for) {
155
- return html`<div class="hidden"><slot></slot></div>`;
156
- }
157
- return html`<span id="link" part="trigger" class="${this.persistent ? 'persistent' : ''}"><slot name="default"><slot></slot></slot></span><span class="hidden"><slot name="alternate"></slot></span>`;
158
- }
159
-
160
- disconnectedCallback() {
161
- super.disconnectedCallback();
162
- if (this._tippy) {
163
- this._tippy.destroy();
164
- }
165
- if (this._observer) {
166
- this._observer.disconnect();
167
- }
168
- }
169
-
170
- _checkCSSProperties() {
171
- if (!this.theme && this.theme !== 'none') {
172
- this.theme = getCSSProperty(this, '--pb-popover-theme', 'none');
173
- }
174
- if (!this.placement) {
175
- this.placement = getCSSProperty(this, '--pb-popover-placement', 'auto');
176
- }
177
- if (!this.fallbackPlacement) {
178
- this.fallbackPlacement = getCSSProperty(this, '--pb-popover-fallback-placement', null);
179
- }
180
- if (!this.persistent) {
181
- this.persistent = getCSSProperty(this, '--pb-popover-persistent', false);
182
- }
183
- if (!this.trigger) {
184
- this.trigger = getCSSProperty(this, '--pb-popover-trigger', null);
185
- }
186
- }
187
-
188
- _injectStyles() {
189
- this._checkCSSProperties();
190
-
191
- loadTippyStyles(this.getRootNode(), this.theme);
192
- }
193
-
194
- _getContent() {
195
- if (this._content) {
196
- return this._content;
197
- }
198
- const slot = this._getSlot();
199
- if (slot) {
200
- const content = document.createElement('div');
201
- slot.assignedNodes().forEach((node) => {
202
- content.appendChild(node.content ? node.content.cloneNode(true) : node.cloneNode(true));
203
- });
204
- this._content = content;
205
- return content;
206
- }
207
- return null;
208
- }
209
-
210
- _getSlot() {
211
- if (this.for) {
212
- return this.shadowRoot.querySelector('slot');
213
- }
214
- return this.shadowRoot.querySelector('[name=alternate]');
215
- }
216
-
217
- /**
218
- * Listen for changes of the current element or its alternate slot
219
- * and update popover content accordingly.
220
- */
221
- _registerMutationObserver() {
222
- const slot = this._getSlot();
223
- this._observer = new MutationObserver(() => {
224
- this.alternate = this._getContent();
225
- console.log('alternate changed');
226
- this.emitTo('pb-popover-changed', this.alternate);
227
- });
228
- this._observer.observe(this, {subtree: true, childList: true, characterData: true})
229
- if (slot) {
230
- slot.assignedNodes().forEach((node) => {
231
- this._observer.observe(node.content ? node.content : node, {subtree: true, childList: true, characterData: true})
232
- });
233
- }
234
- }
235
-
236
- /**
237
- * Returns the root element of the alternate content currently shown in the popover.
238
- * This will be initialized from either the default slot or the slot with name 'alternate' (if present).
239
- * The returned element is always a `div` and can be modified.
240
- */
241
- get alternate() {
242
- return this._getContent();
243
- }
244
-
245
- /**
246
- * Set the element to be shown in the popover. Use this to set popover
247
- * content dynamically. Alternatively you can also modify the DOM of the slots
248
- * directly and the changes should be picked up by the component.
249
- */
250
- set alternate(content) {
251
- this._content = content;
252
- if (this._tippy) {
253
- this._tippy.setContent(this._content);
254
- }
255
- }
256
-
257
- /**
258
- * Overwrite to enable/disable tippy instance
259
- */
260
- command(command, state) {
261
- if (command === 'disable') {
262
- this.disabled = state;
263
- if (this._tippy) {
264
- if (state) {
265
- this._tippy.disable();
266
- } else {
267
- this._tippy.enable();
268
- }
269
- }
270
- }
271
- }
272
-
273
- firstUpdated() {
274
- super.firstUpdated();
275
-
276
- this._injectStyles();
277
-
278
- this._registerMutationObserver();
279
-
280
- if (!this.trigger) {
281
- this.trigger = this.persistent ? 'click' : 'mouseenter';
282
- }
283
-
284
- const root = this.getRootNode();
285
- let target;
286
- if (this.for) {
287
- target = root.getElementById(this.for);
288
- if (!target) {
289
- console.error('<pb-popover> target element %s not found', this.for);
290
- }
291
- } else {
292
- target = this.shadowRoot.getElementById('link');
293
- }
294
- if (target) {
295
- const options = {
296
- allowHTML: true,
297
- appendTo: root.nodeType === Node.DOCUMENT_NODE ? document.body : root,
298
- placement: this.placement,
299
- interactive: true,
300
- ignoreAttributes: true,
301
- boundary: 'viewport',
302
- maxWidth: 'none',
303
- touch: 'hold',
304
- hideOnClick: false,
305
- trigger: this.trigger
306
- };
307
- if (this.stopPropagation) {
308
- options.onTrigger = (instance, ev) => {
309
- ev.stopPropagation();
310
- }
311
- }
312
- if (this.persistent) {
313
- options.onClickOutside = (instance, ev) => {
314
- instance.hideWithInteractivity(ev);
315
- };
316
- }
317
- if (this.theme && this.theme !== 'none') {
318
- options.theme = this.theme;
319
- }
320
- if (this.fallbackPlacement) {
321
- const placements = this.fallbackPlacement.split(' ');
322
- options.popperOptions = {
323
- modifiers: [
324
- {
325
- name: 'flip',
326
- options: {
327
- fallbackPlacements: placements
328
- }
329
- }
330
- ]
331
- }
332
- }
333
- if (this.popupClass) {
334
- options.onCreate = (instance) => {
335
- instance.popper.classList.add(this.popupClass);
336
- };
337
- }
338
- options.onShow = (instance) => {
339
- if (this.remote) {
340
- this._loadRemoteContent();
341
- } else {
342
- instance.setContent(this._getContent());
343
- }
344
- this.emitTo('pb-popover-show', { source: this, popup: instance });
345
- };
346
-
347
- this._tippy = tippy(target, options);
348
- }
349
- }
350
-
351
- _loadRemoteContent() {
352
- const url = this.toAbsoluteURL(this.remote);
353
- fetch(url, {
354
- method: 'GET',
355
- mode: 'cors',
356
- credentials: 'same-origin'
357
- })
358
- .then((response) => response.text())
359
- .then((data) => {
360
- this.alternate = data;
361
- })
362
- .catch((error) => {
363
- console.error('<pb-popover> Error retrieving remote content: %o', error);
364
- });
365
- }
366
-
367
- static get styles() {
368
- return [
369
- css`
370
- :host {
371
- display: inline;
372
- }
373
- .hidden {
374
- display: none;
375
- }
376
- div {
377
- float: left;
378
- }
379
- #link {
380
- display: inline;
381
- color: inherit;
382
- text-decoration: var(--pb-popover-link-decoration, var(--pb-link-text-decoration, inherit));
383
- }
384
- #link.persistent {
385
- cursor: pointer;
386
- }
387
- `
388
- ];
389
- }
390
- }
1
+ import { LitElement, html, css } from 'lit-element';
2
+ import tippy from 'tippy.js';
3
+ import { pbMixin } from './pb-mixin.js';
4
+ import { getCSSProperty } from "./utils.js";
5
+ import * as themes from './pb-popover-themes.js';
6
+
7
+ function _injectStylesheet(root, name, cssCode) {
8
+ const style = root.querySelector(`#pb-popover-${name}`);
9
+ if (!style) {
10
+ const container = root.nodeType === Node.DOCUMENT_NODE ? document.head : root;
11
+ console.log('Loading tippy styles for theme %s into %o', name, container);
12
+ const elem = document.createElement('style');
13
+ elem.type = 'text/css';
14
+ elem.id = `pb-popover-${name}`;
15
+ elem.innerHTML = cssCode;
16
+ container.appendChild(elem);
17
+ }
18
+ }
19
+
20
+ export function loadTippyStyles(root, theme) {
21
+ _injectStylesheet(root, 'base', themes.base);
22
+ if (theme && theme !== 'none') {
23
+ const name = themes.camelize(theme);
24
+ const themeCSS = themes[name];
25
+ if (themeCSS) {
26
+ _injectStylesheet(root, name, themeCSS);
27
+ }
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Show a popover. It may either
33
+ *
34
+ * 1. be attached to another element on the page which serves as a trigger. For this the
35
+ * `for` property must be specified and should contain the ID of the trigger element.
36
+ * The whole content of the `pb-popover` element will be shown in the popup.
37
+ *
38
+ * 2. if no `for` property is specified, the `pb-popover` acts itself as the trigger. The trigger
39
+ * text is either taken from a slot named `default` - or the default slot (i.e. the content of the element).
40
+ * The content to show in the popup should be supplied in a slot named `alternate`. It is recommended to use an
41
+ * HTML `template` to specify the alternate, so it is ignored by the browser:
42
+ *
43
+ * ```html
44
+ * <pb-popover theme="material">
45
+ * <span slot="default">ipsum dolor sit amet</span>
46
+ * <template slot="alternate">
47
+ * <p>At vero eos et <strong>accusam</strong> et justo duo dolores<br>
48
+ * et ea rebum.</p>
49
+ * </template>
50
+ * </pb-popover>
51
+ * ```
52
+ *
53
+ * If you would like popovers to contain nested popovers, choose approach 1 above and use `for`.
54
+ *
55
+ * If property `persistent` is true, the popover will be shown
56
+ * on click. Otherwise display a tooltip on mouseover.
57
+ *
58
+ * `pb-popover` uses the tippy.js library for the popup.
59
+ *
60
+ * ## Styling
61
+ *
62
+ * When showing the popup, the popup content will either be added to the parent shadow DOM - if the `pb-popover`
63
+ * is located inside the shadow DOM of another element like `pb-view`; or the document body. This has an
64
+ * effect on where CSS styles can be defined: within a `pb-view`, only the styles specified inside the
65
+ * CSS attached to the ODD are applied.
66
+ *
67
+ * @prop {String} for - The id of a trigger element (e.g. a link) to which the popover will
68
+ * be attached. If not set, the trigger is the pb-popover itself.
69
+ * @prop {"material" | "light" | "translucent" | "light-border"} theme - The tippy theme to use.
70
+ * @prop {"auto" | "top" | "bottom" | "left" | "right"} placement - Preferred placement of the popup.
71
+ * Default is 'auto'.
72
+ * @prop {String} fallbackPlacement - Fallback placement if there is more space on another side.
73
+ * Accepts same values as `placement`. Separate by space if more than one.
74
+ * @prop {Boolean} persistent - If true, show the 'hand' cursor when hovering over the link; `trigger` will be set to 'click'
75
+ * unless defined otherwise; clicking anywhere on the page will close the popup.
76
+ * @prop {"click" | "mouseenter" | "focus" | "focusin"} trigger - Defines one or more actions (space separated) which should cause
77
+ * the popover to show. If property `persistent` is set, `trigger` will by default be set to `click`.
78
+ * @prop {String} popupClass - Additional class names which will be added to the popup element.
79
+ * Use this to apply a specific style to certain popovers, but not others.
80
+ * @prop {String} remote - An optional URL to asynchronously load the popover's content from. Content will
81
+ * be loaded after the popover is displayed. The downloaded HTML content will replace the text set via the alternate slot.
82
+ * @prop {Boolean} stopPropagation - If you have nested pb-popover, set this property to
83
+ * only show the innermost popover when triggered
84
+ *
85
+ * @slot default - the content to show for the trigger. If not specified, this will fall back to the unnamed slot.
86
+ * @slot alternate - the content to show in the popup
87
+ *
88
+ * @csspart trigger - the inline element used as trigger
89
+ *
90
+ * @cssprop [--pb-popover-theme=none] - popup theme to use. One of 'material', 'light', 'translucent' or 'light-border'
91
+ * @cssprop [--pb-popover-link-decoration=inherit] - text decoration for the trigger
92
+ * @cssprop [--pb-popover-max-height=calc(100vh - 60px)] - limit the maximum height of the popup
93
+ * @cssprop --pb-popover-min-height - set the minimum height of the popup
94
+ * @cssprop --pb-popover-max-width - limit the max width of the popup
95
+ * @cssprop --pb-popover-min-width - set the minimum width of the popup
96
+ * @cssprop --pb-popover-color - Color of the popup text
97
+ * @cssprop [--pb-popover-placement=auto] - Preferred popup placement, see property `placement`
98
+ * @cssprop --pb-popover-fallback-placement - Fallback placements separated by space
99
+ * @cssprop --pb-popover-trigger - define the trigger action, same as property `trigger`
100
+ * @cssprop --pb-popover-persistent - switch to persistent behaviour, see property `persistent`
101
+ */
102
+ export class PbPopover extends pbMixin(LitElement) {
103
+ static get properties() {
104
+ return {
105
+ ...super.properties,
106
+ for: {
107
+ type: String
108
+ },
109
+ theme: {
110
+ type: String
111
+ },
112
+ placement: {
113
+ type: String
114
+ },
115
+ fallbackPlacement: {
116
+ type: String,
117
+ attribute: 'fallback-placement'
118
+ },
119
+ persistent: {
120
+ type: Boolean
121
+ },
122
+ trigger: {
123
+ type: String
124
+ },
125
+ touch: {
126
+ type: String
127
+ },
128
+ popupClass: {
129
+ type: String,
130
+ attribute: 'popup-class'
131
+ },
132
+ remote: {
133
+ type: String
134
+ },
135
+ stopPropagation: {
136
+ type: Boolean,
137
+ attribute: 'stop-propagation'
138
+ }
139
+ };
140
+ }
141
+
142
+ constructor() {
143
+ super();
144
+ this.persistent = false;
145
+ this.trigger = null;
146
+ this.for = null;
147
+ this.theme = null;
148
+ this.placement = null;
149
+ this.touch = null;
150
+ this.fallbackPlacement = null;
151
+ this.popupClass = null;
152
+ this.stopPropagation = false;
153
+ this._tippy = null;
154
+ this._content = null;
155
+ }
156
+
157
+ render() {
158
+ if (this.for) {
159
+ return html`<div class="hidden"><slot></slot></div>`;
160
+ }
161
+ return html`<span id="link" part="trigger" class="${this.persistent ? 'persistent' : ''}"><slot name="default"><slot></slot></slot></span><span class="hidden"><slot name="alternate"></slot></span>`;
162
+ }
163
+
164
+ disconnectedCallback() {
165
+ super.disconnectedCallback();
166
+ if (this._tippy) {
167
+ this._tippy.destroy();
168
+ }
169
+ if (this._observer) {
170
+ this._observer.disconnect();
171
+ }
172
+ }
173
+
174
+ _checkCSSProperties() {
175
+ if (!this.theme && this.theme !== 'none') {
176
+ this.theme = getCSSProperty(this, '--pb-popover-theme', 'none');
177
+ }
178
+ if (!this.placement) {
179
+ this.placement = getCSSProperty(this, '--pb-popover-placement', 'auto');
180
+ }
181
+ if (!this.fallbackPlacement) {
182
+ this.fallbackPlacement = getCSSProperty(this, '--pb-popover-fallback-placement', null);
183
+ }
184
+ if (!this.persistent) {
185
+ this.persistent = getCSSProperty(this, '--pb-popover-persistent', false);
186
+ }
187
+ if (!this.trigger) {
188
+ this.trigger = getCSSProperty(this, '--pb-popover-trigger', null);
189
+ }
190
+ if (!this.touch) {
191
+ const prop = getCSSProperty(this, '--pb-popover-touch', 'hold');
192
+ this.touch = prop === "true" ? true : prop;
193
+ }
194
+ }
195
+
196
+ _injectStyles() {
197
+ this._checkCSSProperties();
198
+
199
+ loadTippyStyles(this.getRootNode(), this.theme);
200
+ }
201
+
202
+ _getContent() {
203
+ if (this._content) {
204
+ return this._content;
205
+ }
206
+ const slot = this._getSlot();
207
+ if (slot) {
208
+ const content = document.createElement('div');
209
+ slot.assignedNodes().forEach((node) => {
210
+ content.appendChild(node.content ? node.content.cloneNode(true) : node.cloneNode(true));
211
+ });
212
+ this._content = content;
213
+ return content;
214
+ }
215
+ return null;
216
+ }
217
+
218
+ _getSlot() {
219
+ if (this.for) {
220
+ return this.shadowRoot.querySelector('slot');
221
+ }
222
+ return this.shadowRoot.querySelector('[name=alternate]');
223
+ }
224
+
225
+ /**
226
+ * Listen for changes of the current element or its alternate slot
227
+ * and update popover content accordingly.
228
+ */
229
+ _registerMutationObserver() {
230
+ const slot = this._getSlot();
231
+ this._observer = new MutationObserver(() => {
232
+ this.alternate = this._getContent();
233
+ console.log('alternate changed');
234
+ this.emitTo('pb-popover-changed', this.alternate);
235
+ });
236
+ this._observer.observe(this, {subtree: true, childList: true, characterData: true})
237
+ if (slot) {
238
+ slot.assignedNodes().forEach((node) => {
239
+ this._observer.observe(node.content ? node.content : node, {subtree: true, childList: true, characterData: true})
240
+ });
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Returns the root element of the alternate content currently shown in the popover.
246
+ * This will be initialized from either the default slot or the slot with name 'alternate' (if present).
247
+ * The returned element is always a `div` and can be modified.
248
+ */
249
+ get alternate() {
250
+ return this._getContent();
251
+ }
252
+
253
+ /**
254
+ * Set the element to be shown in the popover. Use this to set popover
255
+ * content dynamically. Alternatively you can also modify the DOM of the slots
256
+ * directly and the changes should be picked up by the component.
257
+ */
258
+ set alternate(content) {
259
+ this._content = content;
260
+ if (this._tippy) {
261
+ this._tippy.setContent(this._content);
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Overwrite to enable/disable tippy instance
267
+ */
268
+ command(command, state) {
269
+ if (command === 'disable') {
270
+ this.disabled = state;
271
+ if (this._tippy) {
272
+ if (state) {
273
+ this._tippy.disable();
274
+ } else {
275
+ this._tippy.enable();
276
+ }
277
+ }
278
+ }
279
+ }
280
+
281
+ firstUpdated() {
282
+ super.firstUpdated();
283
+
284
+ this._injectStyles();
285
+
286
+ this._registerMutationObserver();
287
+
288
+ if (!this.trigger) {
289
+ this.trigger = this.persistent ? 'click' : 'mouseenter';
290
+ }
291
+
292
+ const root = this.getRootNode();
293
+ let target;
294
+ if (this.for) {
295
+ target = root.getElementById(this.for);
296
+ if (!target) {
297
+ console.error('<pb-popover> target element %s not found', this.for);
298
+ }
299
+ } else {
300
+ target = this.shadowRoot.getElementById('link');
301
+ }
302
+ if (target) {
303
+ const options = {
304
+ allowHTML: true,
305
+ appendTo: root.nodeType === Node.DOCUMENT_NODE ? document.body : root,
306
+ placement: this.placement,
307
+ interactive: true,
308
+ ignoreAttributes: true,
309
+ boundary: 'viewport',
310
+ maxWidth: 'none',
311
+ touch: this.touch,
312
+ hideOnClick: false,
313
+ trigger: this.trigger
314
+ };
315
+ if (this.stopPropagation) {
316
+ options.onTrigger = (instance, ev) => {
317
+ ev.stopPropagation();
318
+ }
319
+ }
320
+ if (this.persistent) {
321
+ options.onClickOutside = (instance, ev) => {
322
+ instance.hideWithInteractivity(ev);
323
+ };
324
+ }
325
+ if (this.theme && this.theme !== 'none') {
326
+ options.theme = this.theme;
327
+ }
328
+ if (this.fallbackPlacement) {
329
+ const placements = this.fallbackPlacement.split(' ');
330
+ options.popperOptions = {
331
+ modifiers: [
332
+ {
333
+ name: 'flip',
334
+ options: {
335
+ fallbackPlacements: placements
336
+ }
337
+ }
338
+ ]
339
+ }
340
+ }
341
+ if (this.popupClass) {
342
+ options.onCreate = (instance) => {
343
+ instance.popper.classList.add(this.popupClass);
344
+ };
345
+ }
346
+ options.onShow = (instance) => {
347
+ if (this.remote) {
348
+ this._loadRemoteContent();
349
+ } else {
350
+ instance.setContent(this._getContent());
351
+ }
352
+ this.emitTo('pb-popover-show', { source: this, popup: instance });
353
+ };
354
+
355
+ this._tippy = tippy(target, options);
356
+ }
357
+ }
358
+
359
+ _loadRemoteContent() {
360
+ const url = this.toAbsoluteURL(this.remote);
361
+ fetch(url, {
362
+ method: 'GET',
363
+ mode: 'cors',
364
+ credentials: 'same-origin'
365
+ })
366
+ .then((response) => response.text())
367
+ .then((data) => {
368
+ this.alternate = data;
369
+ })
370
+ .catch((error) => {
371
+ console.error('<pb-popover> Error retrieving remote content: %o', error);
372
+ });
373
+ }
374
+
375
+ static get styles() {
376
+ return [
377
+ css`
378
+ :host {
379
+ display: inline;
380
+ }
381
+ .hidden {
382
+ display: none;
383
+ }
384
+ div {
385
+ float: left;
386
+ }
387
+ #link {
388
+ display: inline;
389
+ color: inherit;
390
+ text-decoration: var(--pb-popover-link-decoration, var(--pb-link-text-decoration, inherit));
391
+ }
392
+ #link.persistent {
393
+ cursor: pointer;
394
+ }
395
+ `
396
+ ];
397
+ }
398
+ }
391
399
  customElements.define('pb-popover', PbPopover);