@quandis/qbo4.ui 4.0.1-CI-20241107-011209 → 4.0.1-CI-20241108-204652

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.
@@ -7,144 +7,117 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
- import { LitElement, html, css } from 'lit';
11
- import { customElement, property } from 'lit/decorators.js';
12
- import { qboui } from './styles.js';
13
- let QboMenu = class QboMenu extends LitElement {
10
+ import { LitElement, html, css } from "lit";
11
+ import { property, query, state } from "lit/decorators.js";
12
+ import { computePosition, shift, flip } from "@floating-ui/dom";
13
+ export class QboMenu extends LitElement {
14
14
  constructor() {
15
15
  super(...arguments);
16
- this.type = 'side';
17
- this.expanded = false;
18
- /* Css Selector for the HTMLElement that the context menu applies to. */
19
- this.target = '';
20
- this.showContextMenu = (event) => {
16
+ // Define the placement property with type `string`
17
+ this.placement = "bottom-start";
18
+ // Internal state to control menu visibility
19
+ this.isOpen = false;
20
+ this.middleware = [flip(), shift()];
21
+ this.handleMenuOpen = (event) => {
21
22
  event.preventDefault();
22
- if (!this.expanded) {
23
- this.expanded = true;
24
- this.positionContextMenu(event);
23
+ event.stopPropagation();
24
+ const customEvent = event;
25
+ if (customEvent.detail.menu !== this && this.isOpen) {
26
+ this.closeMenu();
25
27
  }
26
28
  };
27
- this.hideContextMenu = (event) => {
28
- if (this.expanded && !this.shadowRoot?.contains(event.target) && !this.contains(event.target)) {
29
- this.expanded = false;
29
+ this.handleOutsideClick = (event) => {
30
+ const customEvent = event;
31
+ const target = customEvent.target;
32
+ if (target !== this && !this.contains(target)) {
33
+ this.closeMenu();
30
34
  }
31
35
  };
32
- this.toggleSideMenu = (event) => {
33
- const target = event.target;
34
- if (target?.closest('menu') == null) {
35
- this.expanded = !this.expanded;
36
- this.classList.toggle('expanded', this.expanded);
36
+ this.toggleMenu = (event) => {
37
+ if (event.target instanceof HTMLElement
38
+ || event.target instanceof SVGElement
39
+ || event.target instanceof SVGUseElement) {
40
+ let toggleMenu = true;
41
+ let parent = event.target.parentElement;
42
+ while (toggleMenu && parent) {
43
+ if (parent instanceof HTMLElement && parent.tagName.toLowerCase() == 'aside')
44
+ toggleMenu = false;
45
+ else
46
+ parent = parent.parentElement;
47
+ }
48
+ if (toggleMenu) {
49
+ this.isOpen = !this.isOpen;
50
+ this.menuContainer.classList.toggle("open", this.isOpen);
51
+ if (this.isOpen) {
52
+ this.updateMenuPosition();
53
+ document.dispatchEvent(new CustomEvent("qbo-menu-open", {
54
+ bubbles: true,
55
+ composed: true,
56
+ detail: { menu: this },
57
+ }));
58
+ }
59
+ }
37
60
  }
38
61
  };
39
62
  }
40
- static { this.styles = [qboui, css `
41
- :host {
42
- display: block;
43
- }
44
-
45
- .toggle-btn {
46
- width: 100%;
47
- background: none;
48
- border: none;
49
- font-size: 1.5rem;
50
- cursor: pointer;
51
- text-align: left;
52
- }
53
- `]; }
54
- toggleSidebar() {
55
- this.expanded = !this.expanded;
56
- }
63
+ static { this.styles = css ``; }
57
64
  connectedCallback() {
58
65
  super.connectedCallback();
59
- switch (this.type) {
60
- case 'context':
61
- document.addEventListener('click', this.hideContextMenu);
62
- this.contextParent?.addEventListener('contextmenu', this.showContextMenu);
63
- break;
64
- case 'side':
65
- this.renderRoot.addEventListener('click', this.toggleSideMenu);
66
- break;
67
- }
66
+ // document.addEventListener("click", this.handleOutsideClick.bind);
67
+ document.addEventListener("qbo-menu-open", this.handleMenuOpen.bind(this));
68
68
  }
69
69
  disconnectedCallback() {
70
70
  super.disconnectedCallback();
71
- switch (this.type) {
72
- case 'context':
73
- this.contextParent?.removeEventListener('contextmenu', this.showContextMenu);
74
- document.removeEventListener('click', this.hideContextMenu);
75
- break;
76
- case 'side':
77
- this.renderRoot.removeEventListener('click', this.toggleSideMenu);
78
- break;
79
- }
80
- }
81
- xcreateRenderRoot() {
82
- return this; // : super.createRenderRoot();
71
+ // document.removeEventListener("click", this.handleOutsideClick);
72
+ document.removeEventListener("qbo-menu-open", this.handleMenuOpen);
73
+ this.triggerButton.removeEventListener("click", this.toggleMenu);
83
74
  }
84
- async positionContextMenu(event) {
85
- event.preventDefault();
86
- this.expanded = true;
87
- await this.updateComplete;
88
- const viewportWidth = window.innerWidth;
89
- const viewportHeight = window.innerHeight;
90
- const menuWidth = this.offsetWidth || 0;
91
- const menuHeight = this.offsetHeight || 0;
92
- // Calculate adjusted x and y positions
93
- let x = event.clientX;
94
- let y = event.clientY;
95
- if (x + menuWidth > viewportWidth) {
96
- x = viewportWidth - menuWidth;
97
- }
98
- if (y + menuHeight > viewportHeight) {
99
- y = viewportHeight - menuHeight;
100
- }
101
- console.log(`mouse event: ${event.clientX} x ${event.clientY} ; ${x} x ${y}`);
102
- this.style.left = x + 'px';
103
- this.style.top = y + 'px';
75
+ firstUpdated(changes) {
76
+ super.firstUpdated(changes);
77
+ this.triggerButton =
78
+ this.querySelector("*[data-trigger]") ||
79
+ this.querySelector("button") ||
80
+ this.parentElement;
81
+ this.menuContainer =
82
+ this.querySelector("*[data-content]") ||
83
+ this.querySelector("aside") ||
84
+ this.querySelector("menu");
85
+ if (this.triggerButton != null)
86
+ this.triggerButton.addEventListener("click", this.toggleMenu);
104
87
  }
105
- get contextParent() {
106
- if (this.target) {
107
- return (this.closest(this.target) ||
108
- (() => {
109
- const rootNode = this.getRootNode();
110
- if (rootNode instanceof ShadowRoot || rootNode instanceof Document) {
111
- return rootNode.querySelector(this.target);
112
- }
113
- return null;
114
- })() ||
115
- document.querySelector(this.target) ||
116
- document);
117
- }
118
- else {
119
- return this.parentElement;
120
- }
121
- }
122
- context() {
123
- return html `${this.expanded ? html `<slot></slot>` : ''}`;
88
+ closeMenu() {
89
+ this.isOpen = false;
90
+ this.menuContainer.classList.remove("open");
124
91
  }
125
- side() {
126
- return html `<slot></slot>`;
92
+ async updateMenuPosition() {
93
+ const { x, y } = await computePosition(this.triggerButton, this.menuContainer, {
94
+ placement: this.placement,
95
+ middleware: this.middleware,
96
+ });
97
+ Object.assign(this.menuContainer.style, {
98
+ left: `${x}px`,
99
+ top: `${y}px`,
100
+ });
127
101
  }
128
102
  render() {
129
- switch (this.type) {
130
- case 'side': return this.side();
131
- case 'context': return this.context();
132
- }
103
+ return html `<slot></slot>`;
133
104
  }
134
- };
105
+ }
135
106
  __decorate([
136
107
  property({ type: String }),
137
108
  __metadata("design:type", String)
138
- ], QboMenu.prototype, "type", void 0);
139
- __decorate([
140
- property({ type: Boolean }),
141
- __metadata("design:type", Boolean)
142
- ], QboMenu.prototype, "expanded", void 0);
109
+ ], QboMenu.prototype, "placement", void 0);
143
110
  __decorate([
144
- property({ type: String }),
111
+ state(),
145
112
  __metadata("design:type", Object)
146
- ], QboMenu.prototype, "target", void 0);
147
- QboMenu = __decorate([
148
- customElement('qbo-menu')
149
- ], QboMenu);
150
- export { QboMenu };
113
+ ], QboMenu.prototype, "isOpen", void 0);
114
+ __decorate([
115
+ query("slot"),
116
+ __metadata("design:type", HTMLSlotElement)
117
+ ], QboMenu.prototype, "slotElement", void 0);
118
+ __decorate([
119
+ query('slot[name="menu"]'),
120
+ __metadata("design:type", HTMLSlotElement)
121
+ ], QboMenu.prototype, "menuSlotElement", void 0);
122
+ if (!customElements.get("qbo-menu"))
123
+ customElements.define("qbo-menu", QboMenu);
@@ -1,151 +1,126 @@
1
- import { LitElement, html, css } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
3
- import { qboui } from './styles.js';
1
+ import { LitElement, PropertyValues, html, css, PropertyValueMap } from "lit";
2
+ import { property, query, state } from "lit/decorators.js";
3
+ import { computePosition, Placement, shift, flip } from "@floating-ui/dom";
4
4
 
5
- @customElement('qbo-menu')
6
5
  export class QboMenu extends LitElement {
7
-
6
+ // Define the placement property with type `string`
8
7
  @property({ type: String })
9
- type: string = 'side';
8
+ placement: Placement = "bottom-start";
10
9
 
11
- @property({ type: Boolean })
12
- expanded: boolean = false;
10
+ // Internal state to control menu visibility
11
+ @state() private isOpen = false;
13
12
 
14
- static styles = [qboui, css`
15
- :host {
16
- display: block;
17
- }
13
+ // Query to find the trigger button and menu container in the shadow DOM
14
+ @query("slot") private slotElement!: HTMLSlotElement;
15
+ @query('slot[name="menu"]') private menuSlotElement!: HTMLSlotElement;
18
16
 
19
- .toggle-btn {
20
- width: 100%;
21
- background: none;
22
- border: none;
23
- font-size: 1.5rem;
24
- cursor: pointer;
25
- text-align: left;
26
- }
27
- `];
17
+ private triggerButton!: HTMLElement;
18
+ private menuContainer!: HTMLElement;
19
+ private middleware = [flip(), shift()];
28
20
 
29
- private toggleSidebar() {
30
- this.expanded = !this.expanded;
31
- }
21
+ static styles = css``;
32
22
 
33
23
  connectedCallback() {
34
24
  super.connectedCallback();
35
- switch (this.type) {
36
- case 'context':
37
- document.addEventListener('click', this.hideContextMenu);
38
- this.contextParent?.addEventListener('contextmenu', this.showContextMenu);
39
- break;
40
- case 'side':
41
- this.renderRoot.addEventListener('click', this.toggleSideMenu);
42
- break;
43
- }
25
+ // document.addEventListener("click", this.handleOutsideClick.bind);
26
+ document.addEventListener("qbo-menu-open", this.handleMenuOpen.bind(this));
44
27
  }
45
28
 
46
29
  disconnectedCallback() {
47
30
  super.disconnectedCallback();
48
- switch (this.type) {
49
- case 'context':
50
- this.contextParent?.removeEventListener('contextmenu', this.showContextMenu);
51
- document.removeEventListener('click', this.hideContextMenu);
52
- break;
53
- case 'side':
54
- this.renderRoot.removeEventListener('click', this.toggleSideMenu);
55
- break;
56
- }
31
+ // document.removeEventListener("click", this.handleOutsideClick);
32
+ document.removeEventListener("qbo-menu-open", this.handleMenuOpen);
33
+ this.triggerButton.removeEventListener("click", this.toggleMenu);
57
34
  }
58
35
 
59
- /* Css Selector for the HTMLElement that the context menu applies to. */
60
- @property({ type: String })
61
- target = '';
62
-
63
- xcreateRenderRoot() {
64
- return this; // : super.createRenderRoot();
65
- }
66
-
67
- private showContextMenu = (event: Event) => {
36
+ handleMenuOpen = (event: Event) => {
68
37
  event.preventDefault();
69
- if (!this.expanded) {
70
- this.expanded = true;
71
- this.positionContextMenu(event as MouseEvent);
38
+ event.stopPropagation();
39
+ const customEvent = event as CustomEvent;
40
+ if (customEvent.detail.menu !== this && this.isOpen) {
41
+ this.closeMenu();
72
42
  }
73
- }
43
+ };
74
44
 
75
- private hideContextMenu = (event: Event) => {
76
- if (this.expanded && !this.shadowRoot?.contains(event.target as Node) && !this.contains(event.target as Node)) {
77
- this.expanded = false;
45
+ handleOutsideClick = (event: Event) => {
46
+ const customEvent = event as PointerEvent;
47
+ const target = customEvent.target as HTMLElement;
48
+ if (target !== this && !this.contains(target)) {
49
+ this.closeMenu();
78
50
  }
51
+ };
52
+
53
+ firstUpdated(changes: PropertyValues) {
54
+ super.firstUpdated(changes);
55
+
56
+ this.triggerButton =
57
+ this.querySelector("*[data-trigger]") ||
58
+ (this.querySelector("button") as HTMLElement) ||
59
+ this.parentElement;
60
+ this.menuContainer =
61
+ this.querySelector("*[data-content]") ||
62
+ this.querySelector("aside") ||
63
+ (this.querySelector("menu") as HTMLElement);
64
+
65
+ if (this.triggerButton != null)
66
+ this.triggerButton.addEventListener("click", this.toggleMenu);
79
67
  }
80
68
 
81
- private async positionContextMenu(event: MouseEvent) {
82
- event.preventDefault();
83
- this.expanded = true;
84
- await this.updateComplete;
85
-
86
- const viewportWidth = window.innerWidth;
87
- const viewportHeight = window.innerHeight;
88
-
89
- const menuWidth = this.offsetWidth || 0;
90
- const menuHeight = this.offsetHeight || 0;
91
-
92
- // Calculate adjusted x and y positions
93
- let x = event.clientX;
94
- let y = event.clientY;
95
-
96
- if (x + menuWidth > viewportWidth) {
97
- x = viewportWidth - menuWidth;
98
- }
99
-
100
- if (y + menuHeight > viewportHeight) {
101
- y = viewportHeight - menuHeight;
102
- }
103
- console.log(`mouse event: ${event.clientX} x ${event.clientY} ; ${x} x ${y}`)
104
-
105
- this.style.left = x + 'px';
106
- this.style.top = y + 'px';
107
- }
108
-
109
- get contextParent(): HTMLElement | Document | null {
110
- if (this.target) {
111
- return (
112
- this.closest(this.target) as HTMLElement ||
113
- (() => {
114
- const rootNode = this.getRootNode();
115
- if (rootNode instanceof ShadowRoot || rootNode instanceof Document) {
116
- return rootNode.querySelector(this.target);
117
- }
118
- return null;
119
- })() ||
120
- document.querySelector(this.target) ||
121
- document
122
- );
123
- } else {
124
- return this.parentElement;
69
+ toggleMenu = (event: Event) => {
70
+ if (event.target instanceof HTMLElement
71
+ || event.target instanceof SVGElement
72
+ || event.target instanceof SVGUseElement) {
73
+ let toggleMenu = true;
74
+ let parent = event.target.parentElement;
75
+ while (toggleMenu && parent) {
76
+ if (parent instanceof HTMLElement && parent.tagName.toLowerCase() == 'aside')
77
+ toggleMenu = false;
78
+ else
79
+ parent = parent.parentElement;
80
+ }
81
+
82
+ if (toggleMenu) {
83
+ this.isOpen = !this.isOpen;
84
+ this.menuContainer.classList.toggle("open", this.isOpen);
85
+
86
+ if (this.isOpen) {
87
+ this.updateMenuPosition();
88
+ document.dispatchEvent(
89
+ new CustomEvent("qbo-menu-open", {
90
+ bubbles: true,
91
+ composed: true,
92
+ detail: { menu: this },
93
+ })
94
+ );
95
+ }
96
+ }
125
97
  }
126
- }
98
+ };
127
99
 
128
- context() {
129
- return html`${this.expanded ? html`<slot></slot>` : ''}`;
100
+ closeMenu() {
101
+ this.isOpen = false;
102
+ this.menuContainer.classList.remove("open");
130
103
  }
131
104
 
132
- toggleSideMenu = (event: Event) => {
133
- const target = event.target as HTMLElement;
134
- if (target?.closest('menu') == null) {
135
- this.expanded = !this.expanded;
136
- this.classList.toggle('expanded', this.expanded);
137
- }
138
- }
139
-
140
- side() {
141
- return html`<slot></slot>`
142
-
105
+ private async updateMenuPosition() {
106
+ const { x, y } = await computePosition(
107
+ this.triggerButton,
108
+ this.menuContainer,
109
+ {
110
+ placement: this.placement as Placement | undefined,
111
+ middleware: this.middleware,
112
+ }
113
+ );
114
+
115
+ Object.assign(this.menuContainer.style, {
116
+ left: `${x}px`,
117
+ top: `${y}px`,
118
+ });
143
119
  }
144
120
 
145
121
  render() {
146
- switch (this.type) {
147
- case 'side': return this.side();
148
- case 'context': return this.context();
149
- }
122
+ return html`<slot></slot>`;
150
123
  }
151
124
  }
125
+
126
+ if (!customElements.get("qbo-menu")) customElements.define("qbo-menu", QboMenu);
@@ -1,21 +1,17 @@
1
- import { PropertyValues } from 'lit';
2
- import { QboFormElement } from './qbo-form-element.js';
3
- export declare class QboPopover extends QboFormElement {
4
- contentAtt: string;
5
- contentStyle: string;
6
- containerAtt: string;
7
- containerValue: string;
8
- defaultContent: string;
9
- htmlAtt: string;
10
- htmlValue: string;
11
- placementAtt: string;
12
- placementValue: string;
1
+ import { LitElement, PropertyValues } from "lit";
2
+ import { Placement } from "@floating-ui/dom";
3
+ export declare class QboPopover extends LitElement {
4
+ placement: Placement;
13
5
  selector: string;
14
- toggleAtt: string;
15
- toggleValue: string;
16
- renderInHost: boolean;
17
- createRenderRoot(): HTMLElement | DocumentFragment;
6
+ private isOpen;
7
+ private triggerElement;
8
+ private middleware;
9
+ private setPosition;
18
10
  connectedCallback(): void;
19
- firstUpdated(changedProperties: PropertyValues): void;
11
+ disconnectedCallback(): void;
12
+ firstUpdated(changes: PropertyValues): void;
13
+ handleMenuOpen: (event: Event) => void;
14
+ toggleMenu: (event: Event) => void;
15
+ closeMenu(): void;
20
16
  render(): import("lit-html").TemplateResult<1>;
21
17
  }