kritzel-stencil 0.4.4 → 0.4.6
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/dist/cjs/kritzel-active-users_47.cjs.entry.js +79 -13
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/stencil.cjs.js +1 -1
- package/dist/collection/classes/core/viewport.class.js +14 -1
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +48 -8
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +84 -3
- package/dist/collection/constants/version.js +1 -1
- package/dist/components/index.js +1226 -1
- package/dist/components/kritzel-active-users.js +6 -1
- package/dist/components/kritzel-avatar.js +6 -1
- package/dist/components/kritzel-awareness-cursors.js +6 -1
- package/dist/components/kritzel-back-to-content.js +6 -1
- package/dist/components/kritzel-brush-style.js +64 -1
- package/dist/components/kritzel-button.js +6 -1
- package/dist/components/kritzel-color-palette.js +6 -1
- package/dist/components/kritzel-color.js +6 -1
- package/dist/components/kritzel-context-menu.js +6 -1
- package/dist/components/kritzel-controls.js +6 -1
- package/dist/components/kritzel-current-user-dialog.js +6 -1
- package/dist/components/kritzel-current-user.js +6 -1
- package/dist/components/kritzel-cursor-trail.js +6 -1
- package/dist/components/kritzel-dialog.js +6 -1
- package/dist/components/kritzel-dropdown.js +6 -1
- package/dist/components/kritzel-editor.js +4404 -1
- package/dist/components/kritzel-engine.js +6 -1
- package/dist/components/kritzel-export.js +6 -1
- package/dist/components/kritzel-font-family.js +6 -1
- package/dist/components/kritzel-font-size.js +6 -1
- package/dist/components/kritzel-font.js +6 -1
- package/dist/components/kritzel-icon.js +6 -1
- package/dist/components/kritzel-input.js +6 -1
- package/dist/components/kritzel-line-endings.js +6 -1
- package/dist/components/kritzel-loading-overlay.js +6 -1
- package/dist/components/kritzel-login-dialog.js +6 -1
- package/dist/components/kritzel-master-detail.js +6 -1
- package/dist/components/kritzel-menu-item.js +6 -1
- package/dist/components/kritzel-menu.js +6 -1
- package/dist/components/kritzel-more-menu.js +6 -1
- package/dist/components/kritzel-notification-card.js +6 -1
- package/dist/components/kritzel-numeric-input.js +6 -1
- package/dist/components/kritzel-opacity-slider.js +6 -1
- package/dist/components/kritzel-pill-tabs.js +6 -1
- package/dist/components/kritzel-portal.js +6 -1
- package/dist/components/kritzel-settings.js +6 -1
- package/dist/components/kritzel-shape-fill.js +6 -1
- package/dist/components/kritzel-share-dialog.js +6 -1
- package/dist/components/kritzel-slide-toggle.js +6 -1
- package/dist/components/kritzel-split-button.js +6 -1
- package/dist/components/kritzel-stroke-size.js +6 -1
- package/dist/components/kritzel-sync-indicator.js +6 -1
- package/dist/components/kritzel-tool-config.js +6 -1
- package/dist/components/kritzel-tooltip.js +6 -1
- package/dist/components/kritzel-utility-panel.js +6 -1
- package/dist/components/kritzel-watermark.js +6 -1
- package/dist/components/kritzel-workspace-manager.js +6 -1
- package/dist/components/kritzel-zoom-panel.js +6 -1
- package/dist/components/p-894OnDQV.js +39 -1
- package/dist/components/p-B43upypT.js +1500 -1
- package/dist/components/p-B5oHmPJb.js +97 -1
- package/dist/components/p-B6t5q-V4.js +63 -1
- package/dist/components/p-B8Xi4Clg.js +43 -1
- package/dist/components/p-BBmP8lVY.js +51726 -0
- package/dist/components/p-BDzlx2hB.js +135 -1
- package/dist/components/p-BFTU3MAI.js +7 -1
- package/dist/components/p-BFYtCsZu.js +56 -1
- package/dist/components/p-BI2ng9I_.js +62 -1
- package/dist/components/p-BTzlrIxe.js +194 -1
- package/dist/components/p-BmyTdQGy.js +1090 -1
- package/dist/components/p-BsANVili.js +150 -1
- package/dist/components/p-BxpKq94F.js +290 -1
- package/dist/components/p-C38P-Vd0.js +446 -1
- package/dist/components/p-C4NrrUfN.js +77 -1
- package/dist/components/p-CW-VyJgK.js +6 -1
- package/dist/components/p-CWRSGYOE.js +57 -1
- package/dist/components/p-CXpv9Rxe.js +106 -1
- package/dist/components/p-Ca-igU1f.js +320 -1
- package/dist/components/p-CdR76C4L.js +165 -1
- package/dist/components/p-Ch42cOeY.js +320 -0
- package/dist/components/p-ClFprFvz.js +209 -1
- package/dist/components/p-Cm33td2O.js +122 -1
- package/dist/components/p-CqLgB8JK.js +91 -1
- package/dist/components/p-Cr682JI_.js +156 -1
- package/dist/components/p-CsvMgk_f.js +62 -1
- package/dist/components/p-CwJwsRR3.js +277 -1
- package/dist/components/p-Cymum41a.js +88 -1
- package/dist/components/p-D09kgc5Q.js +94 -1
- package/dist/components/p-D30zs0kA.js +121 -1
- package/dist/components/p-D3y_Hauo.js +85 -1
- package/dist/components/p-D5jf8wAu.js +142 -1
- package/dist/components/p-D82VaHce.js +53 -1
- package/dist/components/p-DAwPPDMS.js +101 -1
- package/dist/components/p-DEBwmRTC.js +55 -1
- package/dist/components/p-DIazmg6w.js +52 -1
- package/dist/components/p-DJ88BrIW.js +70 -1
- package/dist/components/p-DJkKlUyZ.js +182 -1
- package/dist/components/p-DUYmwrWV.js +3267 -1
- package/dist/components/p-DX2nriaq.js +486 -1
- package/dist/components/p-Dd-pMvlr.js +20 -1
- package/dist/components/p-Df3BwVGy.js +299 -1
- package/dist/components/p-DgJ0ufzr.js +43 -1
- package/dist/components/p-DkUb2czk.js +70 -1
- package/dist/components/p-Dlp84thc.js +57 -1
- package/dist/components/p-JYj3Facr.js +431 -1
- package/dist/components/p-Ka7OWbnk.js +2304 -1
- package/dist/components/p-PSFAfAyd.js +49 -1
- package/dist/components/p-WUdQqYnu.js +118 -1
- package/dist/components/p-djJSnjAX.js +84 -1
- package/dist/components/p-e6Zpc9Vp.js +143 -1
- package/dist/components/p-jGOpkGDl.js +30 -1
- package/dist/components/p-pagW8YF7.js +99 -1
- package/dist/esm/kritzel-active-users_47.entry.js +79 -13
- package/dist/esm/loader.js +1 -1
- package/dist/esm/stencil.js +1 -1
- package/dist/stencil/index.esm.js +1172 -1
- package/dist/stencil/p-5b969a42.entry.js +36109 -0
- package/dist/stencil/p-DQuL1Twl.js +3 -1
- package/dist/stencil/p-Dhio9uis.js +1717 -2
- package/dist/stencil/p-fb32cd8f.entry.js +29 -1
- package/dist/stencil/p-uC7_X6yr.js +30737 -1
- package/dist/stencil/stencil.esm.js +21 -1
- package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +2 -0
- package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +7 -0
- package/dist/types/components.d.ts +10 -0
- package/dist/types/constants/version.d.ts +1 -1
- package/package.json +1 -1
- package/dist/components/p--Oazz-BP.js +0 -9
- package/dist/components/p-Xsiz1D5Q.js +0 -1
- package/dist/stencil/p-9abc21b9.entry.js +0 -9
|
@@ -1 +1,150 @@
|
|
|
1
|
-
import{p as
|
|
1
|
+
import { p as proxyCustomElement, H, c as createEvent, h, d as Host, t as transformTag } from './p-B43upypT.js';
|
|
2
|
+
import { d as defineCustomElement$1 } from './p-CdR76C4L.js';
|
|
3
|
+
|
|
4
|
+
const kritzelMasterDetailCss = () => `:host{display:block;width:100%;height:100%}.master-detail-container{display:flex;width:100%;height:100%;min-height:var(--kritzel-master-detail-min-height, 300px);gap:var(--kritzel-master-detail-gap, 0);background-color:var(--kritzel-master-detail-background-color, transparent)}.master-menu{display:flex;flex-direction:column;width:var(--kritzel-master-detail-menu-width, 200px);min-width:var(--kritzel-master-detail-menu-min-width, 160px);padding:var(--kritzel-master-detail-menu-padding, 8px);background-color:var(--kritzel-master-detail-menu-background-color, #ffffff);border-right:var(--kritzel-master-detail-menu-border-right, 1px solid #ebebeb);gap:var(--kritzel-master-detail-menu-gap, 4px);overflow-y:auto}.menu-item{display:flex;align-items:center;gap:var(--kritzel-master-detail-menu-item-gap, 10px);padding:var(--kritzel-master-detail-menu-item-padding, 10px 12px);border:none;border-radius:var(--kritzel-master-detail-menu-item-border-radius, 6px);background-color:var(--kritzel-master-detail-menu-item-background-color, transparent);color:var(--kritzel-master-detail-menu-item-color, #333333);font-family:inherit;font-size:var(--kritzel-master-detail-menu-item-font-size, 14px);font-weight:var(--kritzel-master-detail-menu-item-font-weight, 400);text-align:left;cursor:var(--kritzel-global-pointer-cursor, pointer);transition:background-color 150ms ease, color 150ms ease;-webkit-tap-highlight-color:transparent}.menu-item:hover:not(.is-disabled){background-color:var(--kritzel-master-detail-menu-item-hover-background-color, #ebebeb)}.menu-item:active:not(.is-disabled){background-color:var(--kritzel-master-detail-menu-item-active-background-color, #e0e0e0)}.menu-item.is-selected{background-color:var(--kritzel-master-detail-menu-item-selected-background-color, #0066cc);color:var(--kritzel-master-detail-menu-item-selected-color, #ffffff);font-weight:var(--kritzel-master-detail-menu-item-selected-font-weight, 500)}.menu-item.is-selected:hover{background-color:var(--kritzel-master-detail-menu-item-selected-hover-background-color, #0052a3)}.menu-item.is-disabled{color:var(--kritzel-master-detail-menu-item-disabled-color, #999999);cursor:not-allowed;opacity:0.6}.menu-item:focus-visible{outline:var(--kritzel-master-detail-menu-item-focus-outline, revert);outline-offset:var(--kritzel-master-detail-menu-item-focus-outline-offset, revert)}.menu-item-icon{flex-shrink:0;display:flex;align-items:center;justify-content:center}.menu-item-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.detail-panel{flex:1;min-height:0;padding:var(--kritzel-master-detail-detail-padding, 16px);background-color:var(--kritzel-master-detail-detail-background-color, #ffffff);overflow-y:auto;-webkit-overflow-scrolling:touch;scrollbar-color:var(--kritzel-global-scrollbar-thumb-color, #ebebeb) transparent;scrollbar-width:thin}.detail-panel:focus{outline:none}.detail-panel:focus-visible{outline:var(--kritzel-master-detail-detail-focus-outline, revert);outline-offset:var(--kritzel-master-detail-detail-focus-outline-offset, revert)}.mobile-back-button{display:none;align-items:center;gap:8px;background:none;border:none;padding:0 0 16px 0;cursor:pointer;-webkit-tap-highlight-color:transparent;color:var(--kritzel-master-detail-back-button-color, #333333);font-weight:500;font-size:14px;font-family:inherit}.mobile-back-icon{display:flex;align-items:center;flex-shrink:0}.menu-item-chevron{display:none;flex-shrink:0;margin-left:auto;color:var(--kritzel-master-detail-menu-item-chevron-color, #999999)}.menu-item-chevron svg{width:16px;height:16px;stroke:currentColor}@media (max-width: 768px){.master-menu{width:100%;border-right:none}.detail-panel{display:none;width:100%}.master-detail-container.is-mobile-detail-visible .master-menu{display:none}.master-detail-container.is-mobile-detail-visible .detail-panel{display:block}.mobile-back-button{display:flex}.menu-item.is-selected{background-color:transparent;color:var(--kritzel-master-detail-menu-item-color, #333333);font-weight:var(--kritzel-master-detail-menu-item-font-weight, 400)}.menu-item.is-selected:hover{background-color:var(--kritzel-master-detail-menu-item-hover-background-color, #ebebeb)}.menu-item-chevron{display:flex;align-items:center}}`;
|
|
5
|
+
|
|
6
|
+
const KritzelMasterDetail = /*@__PURE__*/ proxyCustomElement(class KritzelMasterDetail extends H {
|
|
7
|
+
constructor(registerHost) {
|
|
8
|
+
super();
|
|
9
|
+
if (registerHost !== false) {
|
|
10
|
+
this.__registerHost();
|
|
11
|
+
}
|
|
12
|
+
this.__attachShadow();
|
|
13
|
+
this.itemSelect = createEvent(this, "itemSelect");
|
|
14
|
+
}
|
|
15
|
+
get host() { return this; }
|
|
16
|
+
/** Array of menu items to display in the master (left) panel */
|
|
17
|
+
items = [];
|
|
18
|
+
/** ID of the currently selected item */
|
|
19
|
+
selectedItemId;
|
|
20
|
+
/** Emitted when an item is selected */
|
|
21
|
+
itemSelect;
|
|
22
|
+
focusedIndex = -1;
|
|
23
|
+
showMobileDetail = false;
|
|
24
|
+
tabRefs = [];
|
|
25
|
+
watchSelectedItemId(newValue, oldValue) {
|
|
26
|
+
if (newValue !== oldValue) {
|
|
27
|
+
this.showMobileDetail = true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
componentWillRender() {
|
|
31
|
+
this.tabRefs = [];
|
|
32
|
+
}
|
|
33
|
+
handleItemClick = (item) => {
|
|
34
|
+
if (item.disabled)
|
|
35
|
+
return;
|
|
36
|
+
this.itemSelect.emit({ item });
|
|
37
|
+
this.showMobileDetail = true;
|
|
38
|
+
};
|
|
39
|
+
handleBackClick = () => {
|
|
40
|
+
this.showMobileDetail = false;
|
|
41
|
+
};
|
|
42
|
+
handleKeyDown = (event, item, index) => {
|
|
43
|
+
const enabledIndices = this.items
|
|
44
|
+
.map((it, idx) => (!it.disabled ? idx : -1))
|
|
45
|
+
.filter(idx => idx !== -1);
|
|
46
|
+
const currentEnabledIndex = enabledIndices.indexOf(index);
|
|
47
|
+
switch (event.key) {
|
|
48
|
+
case 'ArrowDown': {
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
const nextIndex = enabledIndices[(currentEnabledIndex + 1) % enabledIndices.length];
|
|
51
|
+
this.focusTab(nextIndex);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case 'ArrowUp': {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
const prevIndex = enabledIndices[(currentEnabledIndex - 1 + enabledIndices.length) % enabledIndices.length];
|
|
57
|
+
this.focusTab(prevIndex);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case 'Home': {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
this.focusTab(enabledIndices[0]);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case 'End': {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
this.focusTab(enabledIndices[enabledIndices.length - 1]);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case 'Enter':
|
|
71
|
+
case ' ': {
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
this.handleItemClick(item);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
focusTab(index) {
|
|
79
|
+
this.focusedIndex = index;
|
|
80
|
+
const tab = this.tabRefs[index];
|
|
81
|
+
if (tab) {
|
|
82
|
+
tab.focus();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
handleFocus = (index) => {
|
|
86
|
+
this.focusedIndex = index;
|
|
87
|
+
};
|
|
88
|
+
handleBlur = () => {
|
|
89
|
+
this.focusedIndex = -1;
|
|
90
|
+
};
|
|
91
|
+
setTabRef = (el, index) => {
|
|
92
|
+
if (el) {
|
|
93
|
+
this.tabRefs[index] = el;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
getTabIndex(item, _index) {
|
|
97
|
+
if (item.disabled)
|
|
98
|
+
return -1;
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
render() {
|
|
102
|
+
const selectedItem = this.items.find(item => item.id === this.selectedItemId);
|
|
103
|
+
const panelId = 'master-detail-panel';
|
|
104
|
+
const selectedTabId = selectedItem ? `tab-${selectedItem.id}` : undefined;
|
|
105
|
+
return (h(Host, { key: 'e363142a5175b1f711cebfc98cfeedf07cd2c6c3' }, h("div", { key: 'eebf863ecdb4a53289cb56e14c02977375781167', class: {
|
|
106
|
+
'master-detail-container': true,
|
|
107
|
+
'is-mobile-detail-visible': this.showMobileDetail,
|
|
108
|
+
} }, h("nav", { key: '06660d3cd7de29ba46e3437169f99cb00eef1cc6', class: "master-menu", role: "tablist", "aria-orientation": "vertical", "aria-label": "Settings categories" }, this.items.map((item, index) => (h("button", { key: item.id, id: `tab-${item.id}`, ref: el => this.setTabRef(el, index), class: {
|
|
109
|
+
'menu-item': true,
|
|
110
|
+
'is-selected': item.id === this.selectedItemId,
|
|
111
|
+
'is-disabled': !!item.disabled,
|
|
112
|
+
'is-focused': index === this.focusedIndex,
|
|
113
|
+
}, role: "tab", "aria-selected": item.id === this.selectedItemId ? 'true' : 'false', "aria-controls": panelId, "aria-disabled": item.disabled ? 'true' : undefined, tabIndex: this.getTabIndex(item, index), disabled: item.disabled, onClick: () => this.handleItemClick(item), onKeyDown: e => this.handleKeyDown(e, item, index), onFocus: () => this.handleFocus(index), onBlur: this.handleBlur }, item.icon && (h("kritzel-icon", { name: item.icon, size: 20, class: "menu-item-icon" })), h("span", { class: "menu-item-label" }, item.label), h("span", { class: "menu-item-chevron", "aria-hidden": "true" }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { d: "m9 18 6-6-6-6" }))))))), h("div", { key: '0d6b6722ecbf64b28d27754161f861edafa3882c', id: panelId, class: "detail-panel", role: "tabpanel", "aria-labelledby": selectedTabId }, h("button", { key: '3d8ac88c0f8a7a840b5560c4917d74ff910a99c8', class: "mobile-back-button", onClick: this.handleBackClick, "aria-label": "Back to menu" }, h("kritzel-icon", { key: '03cf6ca5ea2b9d16ff21b1b38fd0b6148d35ff1f', name: "chevron-left", size: 20, class: "mobile-back-icon" }), "Back"), h("slot", { key: '3323357889024c0d2126c30d976c3ece7ddd706a' })))));
|
|
114
|
+
}
|
|
115
|
+
static get watchers() { return {
|
|
116
|
+
"selectedItemId": [{
|
|
117
|
+
"watchSelectedItemId": 0
|
|
118
|
+
}]
|
|
119
|
+
}; }
|
|
120
|
+
static get style() { return kritzelMasterDetailCss(); }
|
|
121
|
+
}, [769, "kritzel-master-detail", {
|
|
122
|
+
"items": [16],
|
|
123
|
+
"selectedItemId": [1, "selected-item-id"],
|
|
124
|
+
"focusedIndex": [32],
|
|
125
|
+
"showMobileDetail": [32]
|
|
126
|
+
}, undefined, {
|
|
127
|
+
"selectedItemId": [{
|
|
128
|
+
"watchSelectedItemId": 0
|
|
129
|
+
}]
|
|
130
|
+
}]);
|
|
131
|
+
function defineCustomElement() {
|
|
132
|
+
if (typeof customElements === "undefined") {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const components = ["kritzel-master-detail", "kritzel-icon"];
|
|
136
|
+
components.forEach(tagName => { switch (tagName) {
|
|
137
|
+
case "kritzel-master-detail":
|
|
138
|
+
if (!customElements.get(transformTag(tagName))) {
|
|
139
|
+
customElements.define(transformTag(tagName), KritzelMasterDetail);
|
|
140
|
+
}
|
|
141
|
+
break;
|
|
142
|
+
case "kritzel-icon":
|
|
143
|
+
if (!customElements.get(transformTag(tagName))) {
|
|
144
|
+
defineCustomElement$1();
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
} });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export { KritzelMasterDetail as K, defineCustomElement as d };
|
|
@@ -1 +1,290 @@
|
|
|
1
|
-
import{p as
|
|
1
|
+
import { p as proxyCustomElement, H, c as createEvent, h, d as Host, t as transformTag } from './p-B43upypT.js';
|
|
2
|
+
import { d as defineCustomElement$1 } from './p-CdR76C4L.js';
|
|
3
|
+
|
|
4
|
+
const kritzelContextMenuCss = () => `:host{display:block}.menu-container{position:relative;z-index:1;display:flex;flex-direction:column;gap:var(--kritzel-context-menu-item-spacing, 2px);background-color:var(--kritzel-context-menu-background-color, #ffffff);border-radius:var(--kritzel-context-menu-border-radius, 12px);box-shadow:var(--kritzel-context-menu-box-shadow, 0 1px 6px rgba(0, 0, 0, 0.12));border:var(--kritzel-context-menu-border, 1px solid #ebebeb);padding:var(--kritzel-context-menu-padding, 4px);min-width:var(--kritzel-context-menu-min-width, 140px)}.menu-item-wrapper{position:relative}.menu-item{display:flex;align-items:center;gap:var(--kritzel-context-menu-item-gap, 8px);background:none;border:none;text-align:left;padding:var(--kritzel-context-menu-item-padding, 8px);border-radius:var(--kritzel-context-menu-item-border-radius, 12px);cursor:var(--kritzel-global-pointer-cursor, pointer);font-size:var(--kritzel-context-menu-item-font-size, 14px);color:var(--kritzel-context-menu-item-color, #333333);white-space:nowrap;-webkit-tap-highlight-color:transparent;width:100%;min-width:0}.menu-item:not(.disabled):hover,.menu-item.submenu-open{background-color:var(--kritzel-context-menu-item-hover-background-color, hsl(0, 0%, 0%, 4.3%))}.menu-item:not(.disabled):active{background-color:var(--kritzel-context-menu-item-active-background-color, hsl(0, 0%, 0%, 8.6%))}.menu-item.disabled{color:var(--kritzel-context-menu-item-disabled-color, #aaaaaa)}.menu-item.has-children.disabled{cursor:var(--kritzel-global-pointer-cursor, pointer);color:var(--kritzel-context-menu-item-color, #333333)}.menu-item kritzel-icon{opacity:0.8;flex-shrink:0}.menu-item.disabled kritzel-icon{opacity:0.4}.menu-item.has-children.disabled kritzel-icon{opacity:0.8}.label{flex-grow:1;min-width:0;overflow:hidden;text-overflow:ellipsis}.submenu-arrow{margin-left:auto;opacity:0.5}.menu-divider{height:1px;background-color:var(--kritzel-context-menu-divider-color, rgba(0, 0, 0, 0.1));margin:var(--kritzel-context-menu-divider-margin, 4px 8px)}.submenu-container{position:absolute;top:0;left:100%;z-index:2;margin-left:4px;display:flex;flex-direction:column;gap:var(--kritzel-context-menu-item-spacing, 2px);background-color:var(--kritzel-context-menu-background-color, #ffffff);border-radius:var(--kritzel-context-menu-border-radius, 12px);box-shadow:var(--kritzel-context-menu-box-shadow, 0 1px 6px rgba(0, 0, 0, 0.12));border:var(--kritzel-context-menu-border, 1px solid #ebebeb);padding:var(--kritzel-context-menu-padding, 4px);min-width:120px}.submenu-container.position-left{left:auto;right:100%;margin-left:0;margin-right:4px}`;
|
|
5
|
+
|
|
6
|
+
const VIEWPORT_PADDING = 8;
|
|
7
|
+
const SUBMENU_DELAY = 150;
|
|
8
|
+
const ESTIMATED_SUBMENU_WIDTH = 160; // Estimated width for pre-calculation
|
|
9
|
+
const KritzelContextMenu = /*@__PURE__*/ proxyCustomElement(class KritzelContextMenu extends H {
|
|
10
|
+
constructor(registerHost) {
|
|
11
|
+
super();
|
|
12
|
+
if (registerHost !== false) {
|
|
13
|
+
this.__registerHost();
|
|
14
|
+
}
|
|
15
|
+
this.__attachShadow();
|
|
16
|
+
this.actionSelected = createEvent(this, "actionSelected");
|
|
17
|
+
this.close = createEvent(this, "close");
|
|
18
|
+
}
|
|
19
|
+
get host() { return this; }
|
|
20
|
+
items;
|
|
21
|
+
onItemsChanged() {
|
|
22
|
+
this.updateMenuItems();
|
|
23
|
+
}
|
|
24
|
+
objects;
|
|
25
|
+
actionSelected;
|
|
26
|
+
close;
|
|
27
|
+
processedItems = [];
|
|
28
|
+
/** Current open submenu path (e.g. '0.2.1'). Empty if none. */
|
|
29
|
+
openSubmenuPath = '';
|
|
30
|
+
submenuPositions = {};
|
|
31
|
+
submenuTimer = null;
|
|
32
|
+
submenuRefs = new Map();
|
|
33
|
+
menuItemWrapperRefs = new Map();
|
|
34
|
+
handleOutsideClick(event) {
|
|
35
|
+
const path = event.composedPath();
|
|
36
|
+
if (!path.includes(this.host)) {
|
|
37
|
+
this.close.emit();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
componentWillLoad() {
|
|
41
|
+
this.updateMenuItems();
|
|
42
|
+
}
|
|
43
|
+
componentDidLoad() {
|
|
44
|
+
this.adjustPositionToViewport();
|
|
45
|
+
}
|
|
46
|
+
componentDidUpdate() {
|
|
47
|
+
this.adjustPositionToViewport();
|
|
48
|
+
this.adjustSubmenuPositions();
|
|
49
|
+
this.pruneStaleRefs();
|
|
50
|
+
}
|
|
51
|
+
disconnectedCallback() {
|
|
52
|
+
if (this.submenuTimer) {
|
|
53
|
+
clearTimeout(this.submenuTimer);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
adjustPositionToViewport() {
|
|
57
|
+
const rect = this.host.getBoundingClientRect();
|
|
58
|
+
const viewportWidth = window.innerWidth;
|
|
59
|
+
const viewportHeight = window.innerHeight;
|
|
60
|
+
const currentLeft = parseFloat(this.host.style.left) || 0;
|
|
61
|
+
const currentTop = parseFloat(this.host.style.top) || 0;
|
|
62
|
+
let newLeft = currentLeft;
|
|
63
|
+
let newTop = currentTop;
|
|
64
|
+
// Check if menu extends beyond right edge
|
|
65
|
+
if (rect.right > viewportWidth - VIEWPORT_PADDING) {
|
|
66
|
+
newLeft = viewportWidth - rect.width - VIEWPORT_PADDING;
|
|
67
|
+
}
|
|
68
|
+
// Check if menu extends beyond bottom edge
|
|
69
|
+
if (rect.bottom > viewportHeight - VIEWPORT_PADDING) {
|
|
70
|
+
newTop = viewportHeight - rect.height - VIEWPORT_PADDING;
|
|
71
|
+
}
|
|
72
|
+
// Ensure menu doesn't go beyond left edge
|
|
73
|
+
if (newLeft < VIEWPORT_PADDING) {
|
|
74
|
+
newLeft = VIEWPORT_PADDING;
|
|
75
|
+
}
|
|
76
|
+
// Ensure menu doesn't go beyond top edge
|
|
77
|
+
if (newTop < VIEWPORT_PADDING) {
|
|
78
|
+
newTop = VIEWPORT_PADDING;
|
|
79
|
+
}
|
|
80
|
+
// Only update if position changed to avoid infinite loops
|
|
81
|
+
if (newLeft !== currentLeft || newTop !== currentTop) {
|
|
82
|
+
this.host.style.left = `${newLeft}px`;
|
|
83
|
+
this.host.style.top = `${newTop}px`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
adjustSubmenuPositions() {
|
|
87
|
+
if (!this.openSubmenuPath)
|
|
88
|
+
return;
|
|
89
|
+
const viewportHeight = window.innerHeight;
|
|
90
|
+
// Adjust every open submenu in the chain (every prefix of openSubmenuPath).
|
|
91
|
+
for (const path of this.getOpenSubmenuPaths()) {
|
|
92
|
+
const submenuEl = this.submenuRefs.get(path);
|
|
93
|
+
const wrapperEl = this.menuItemWrapperRefs.get(path);
|
|
94
|
+
if (!submenuEl || !wrapperEl)
|
|
95
|
+
continue;
|
|
96
|
+
const wrapperRect = wrapperEl.getBoundingClientRect();
|
|
97
|
+
const submenuHeight = submenuEl.offsetHeight; // Constant regardless of current top shift
|
|
98
|
+
const naturalBottom = wrapperRect.top + submenuHeight;
|
|
99
|
+
if (naturalBottom > viewportHeight - VIEWPORT_PADDING) {
|
|
100
|
+
let overflow = naturalBottom - (viewportHeight - VIEWPORT_PADDING);
|
|
101
|
+
// Don't shift up so far that the top goes above the viewport
|
|
102
|
+
if (wrapperRect.top - overflow < VIEWPORT_PADDING) {
|
|
103
|
+
overflow = wrapperRect.top - VIEWPORT_PADDING;
|
|
104
|
+
}
|
|
105
|
+
submenuEl.style.top = `${-overflow}px`;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
submenuEl.style.top = '0px';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
getOpenSubmenuPaths() {
|
|
113
|
+
if (!this.openSubmenuPath)
|
|
114
|
+
return [];
|
|
115
|
+
const parts = this.openSubmenuPath.split('.');
|
|
116
|
+
const paths = [];
|
|
117
|
+
for (let i = 1; i <= parts.length; i++) {
|
|
118
|
+
paths.push(parts.slice(0, i).join('.'));
|
|
119
|
+
}
|
|
120
|
+
return paths;
|
|
121
|
+
}
|
|
122
|
+
isSubmenuOpen(path) {
|
|
123
|
+
return this.openSubmenuPath === path || this.openSubmenuPath.startsWith(path + '.');
|
|
124
|
+
}
|
|
125
|
+
getParentPath(path) {
|
|
126
|
+
const idx = path.lastIndexOf('.');
|
|
127
|
+
return idx === -1 ? '' : path.substring(0, idx);
|
|
128
|
+
}
|
|
129
|
+
getMenuDepth(path) {
|
|
130
|
+
if (!path)
|
|
131
|
+
return 0;
|
|
132
|
+
return path.split('.').length;
|
|
133
|
+
}
|
|
134
|
+
pruneStaleRefs() {
|
|
135
|
+
const openPaths = new Set(this.getOpenSubmenuPaths());
|
|
136
|
+
for (const key of Array.from(this.submenuRefs.keys())) {
|
|
137
|
+
if (!openPaths.has(key)) {
|
|
138
|
+
this.submenuRefs.delete(key);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Keep root-level wrapper refs (single-segment paths) always rendered; prune deeper
|
|
142
|
+
// wrappers whose parent submenu is no longer open.
|
|
143
|
+
for (const key of Array.from(this.menuItemWrapperRefs.keys())) {
|
|
144
|
+
const parent = this.getParentPath(key);
|
|
145
|
+
if (parent !== '' && !openPaths.has(parent)) {
|
|
146
|
+
this.menuItemWrapperRefs.delete(key);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
handleItemClick(item, isDisabled, hasChildren) {
|
|
151
|
+
if (isDisabled)
|
|
152
|
+
return;
|
|
153
|
+
if (hasChildren) {
|
|
154
|
+
// Hover handles open/close; click on a parent is a no-op.
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (item.action) {
|
|
158
|
+
this.actionSelected.emit(item);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
handleItemMouseEnter(path, hasChildren) {
|
|
162
|
+
if (this.submenuTimer) {
|
|
163
|
+
clearTimeout(this.submenuTimer);
|
|
164
|
+
this.submenuTimer = null;
|
|
165
|
+
}
|
|
166
|
+
const parentPath = this.getParentPath(path);
|
|
167
|
+
if (hasChildren) {
|
|
168
|
+
this.submenuTimer = setTimeout(() => {
|
|
169
|
+
// Pre-calculate horizontal position before opening to avoid flicker.
|
|
170
|
+
const wrapperEl = this.menuItemWrapperRefs.get(path);
|
|
171
|
+
let position = 'right';
|
|
172
|
+
if (wrapperEl) {
|
|
173
|
+
const rect = wrapperEl.getBoundingClientRect();
|
|
174
|
+
const viewportWidth = window.innerWidth;
|
|
175
|
+
const wouldOverflowRight = rect.right + ESTIMATED_SUBMENU_WIDTH > viewportWidth - VIEWPORT_PADDING;
|
|
176
|
+
position = wouldOverflowRight ? 'left' : 'right';
|
|
177
|
+
}
|
|
178
|
+
this.submenuPositions = { ...this.submenuPositions, [path]: position };
|
|
179
|
+
this.openSubmenuPath = path;
|
|
180
|
+
}, SUBMENU_DELAY);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
// Hovering a sibling without children: collapse to the parent chain so any
|
|
184
|
+
// sibling-rooted submenu closes, but the ancestor chain stays open.
|
|
185
|
+
this.submenuTimer = setTimeout(() => {
|
|
186
|
+
this.openSubmenuPath = parentPath;
|
|
187
|
+
}, SUBMENU_DELAY);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
handleSubmenuMouseEnter() {
|
|
191
|
+
if (this.submenuTimer) {
|
|
192
|
+
clearTimeout(this.submenuTimer);
|
|
193
|
+
this.submenuTimer = null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
handleSubmenuMouseLeave(path) {
|
|
197
|
+
// Close this submenu (and any deeper levels) but keep the ancestor chain open.
|
|
198
|
+
const parentPath = this.getParentPath(path);
|
|
199
|
+
this.submenuTimer = setTimeout(() => {
|
|
200
|
+
this.openSubmenuPath = parentPath;
|
|
201
|
+
}, SUBMENU_DELAY);
|
|
202
|
+
}
|
|
203
|
+
async updateMenuItems() {
|
|
204
|
+
this.processedItems = await this.processItems(this.items);
|
|
205
|
+
}
|
|
206
|
+
async processItems(items) {
|
|
207
|
+
const processed = [];
|
|
208
|
+
for (const item of items) {
|
|
209
|
+
const isVisible = await this.evaluateProperty(item.visible, true);
|
|
210
|
+
if (isVisible) {
|
|
211
|
+
const isDisabled = await this.evaluateProperty(item.disabled, false);
|
|
212
|
+
let processedChildren;
|
|
213
|
+
if (item.children && item.children.length > 0) {
|
|
214
|
+
processedChildren = await this.processItems(item.children);
|
|
215
|
+
}
|
|
216
|
+
processed.push({ item, isDisabled, processedChildren });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return processed;
|
|
220
|
+
}
|
|
221
|
+
async evaluateProperty(value, defaultValue) {
|
|
222
|
+
if (typeof value === 'boolean') {
|
|
223
|
+
return value;
|
|
224
|
+
}
|
|
225
|
+
if (typeof value === 'function') {
|
|
226
|
+
return await Promise.resolve(value(null, this.objects));
|
|
227
|
+
}
|
|
228
|
+
return defaultValue;
|
|
229
|
+
}
|
|
230
|
+
renderItems(items, parentPath) {
|
|
231
|
+
return items.map(({ item, isDisabled, processedChildren }, index) => {
|
|
232
|
+
const path = parentPath === '' ? String(index) : `${parentPath}.${index}`;
|
|
233
|
+
const prevItem = index > 0 ? items[index - 1].item : null;
|
|
234
|
+
const showDivider = prevItem && prevItem.group !== item.group;
|
|
235
|
+
const hasChildren = !!processedChildren && processedChildren.length > 0;
|
|
236
|
+
const submenuOpen = hasChildren && this.isSubmenuOpen(path);
|
|
237
|
+
return [
|
|
238
|
+
showDivider && h("div", { class: "menu-divider", key: `divider-${path}` }),
|
|
239
|
+
h("div", { class: "menu-item-wrapper", key: `wrapper-${path}`, ref: el => el && this.menuItemWrapperRefs.set(path, el), onMouseEnter: () => this.handleItemMouseEnter(path, hasChildren) }, h("button", { key: `${item.label}-${path}`, class: { 'menu-item': true, 'disabled': isDisabled, 'has-children': hasChildren, 'submenu-open': submenuOpen }, onClick: () => this.handleItemClick(item, isDisabled, hasChildren), disabled: isDisabled && !hasChildren }, item.icon && h("kritzel-icon", { name: item.icon, size: 16 }), h("span", { class: "label" }, item.label), hasChildren && h("kritzel-icon", { name: "chevron-right", size: 12, class: "submenu-arrow" })), hasChildren && submenuOpen && this.renderSubmenu(processedChildren, path)),
|
|
240
|
+
];
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
renderSubmenu(processedChildren, path) {
|
|
244
|
+
const position = this.submenuPositions[path] === 'left' ? 'left' : 'right';
|
|
245
|
+
const zIndex = this.getMenuDepth(path) + 1;
|
|
246
|
+
return (h("div", { class: { 'submenu-container': true, 'position-left': position === 'left' }, key: `submenu-${path}`, style: { zIndex: String(zIndex) }, ref: el => el && this.submenuRefs.set(path, el), onMouseEnter: () => this.handleSubmenuMouseEnter(), onMouseLeave: () => this.handleSubmenuMouseLeave(path) }, this.renderItems(processedChildren, path)));
|
|
247
|
+
}
|
|
248
|
+
render() {
|
|
249
|
+
if (!this.processedItems || this.processedItems.length === 0) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
return (h(Host, null, h("div", { class: "menu-container" }, this.renderItems(this.processedItems, ''))));
|
|
253
|
+
}
|
|
254
|
+
static get watchers() { return {
|
|
255
|
+
"items": [{
|
|
256
|
+
"onItemsChanged": 0
|
|
257
|
+
}]
|
|
258
|
+
}; }
|
|
259
|
+
static get style() { return kritzelContextMenuCss(); }
|
|
260
|
+
}, [513, "kritzel-context-menu", {
|
|
261
|
+
"items": [16],
|
|
262
|
+
"objects": [16],
|
|
263
|
+
"processedItems": [32],
|
|
264
|
+
"openSubmenuPath": [32],
|
|
265
|
+
"submenuPositions": [32]
|
|
266
|
+
}, [[9, "pointerdown", "handleOutsideClick"]], {
|
|
267
|
+
"items": [{
|
|
268
|
+
"onItemsChanged": 0
|
|
269
|
+
}]
|
|
270
|
+
}]);
|
|
271
|
+
function defineCustomElement() {
|
|
272
|
+
if (typeof customElements === "undefined") {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const components = ["kritzel-context-menu", "kritzel-icon"];
|
|
276
|
+
components.forEach(tagName => { switch (tagName) {
|
|
277
|
+
case "kritzel-context-menu":
|
|
278
|
+
if (!customElements.get(transformTag(tagName))) {
|
|
279
|
+
customElements.define(transformTag(tagName), KritzelContextMenu);
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
case "kritzel-icon":
|
|
283
|
+
if (!customElements.get(transformTag(tagName))) {
|
|
284
|
+
defineCustomElement$1();
|
|
285
|
+
}
|
|
286
|
+
break;
|
|
287
|
+
} });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export { KritzelContextMenu as K, defineCustomElement as d };
|