@rivinia/menu 0.1.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +687 -47
- package/fesm2022/rivinia-menu-ui.mjs +227 -22
- package/fesm2022/rivinia-menu-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/rivinia-menu-ui.d.ts +95 -4
- package/types/rivinia-menu.d.ts +13 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
|
-
import { Component, Directive, ElementRef, ViewEncapsulation, afterRenderEffect, computed, effect, inject, input, output, signal } from "@angular/core";
|
|
2
|
+
import { Component, Directive, ElementRef, ViewEncapsulation, afterRenderEffect, computed, contentChildren, effect, inject, input, output, signal } from "@angular/core";
|
|
3
3
|
import { RiviniaMenu } from "@rivinia/menu";
|
|
4
4
|
const MENU_GAP = 6;
|
|
5
5
|
const EDGE = 8;
|
|
@@ -31,6 +31,7 @@ function computeMenuPosition(trigger, panel, placement, viewport, rtl, gap = 6)
|
|
|
31
31
|
flipped
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
+
let nextPanelId = 0;
|
|
34
35
|
var RiviniaMenuItem = class RiviniaMenuItem {
|
|
35
36
|
id = input.required(...ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []);
|
|
36
37
|
disabled = input(false, {
|
|
@@ -38,6 +39,7 @@ var RiviniaMenuItem = class RiviniaMenuItem {
|
|
|
38
39
|
transform: (v) => v === "" || v === true
|
|
39
40
|
});
|
|
40
41
|
checked = input(null, ...ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []);
|
|
42
|
+
host = inject(ElementRef, { optional: true });
|
|
41
43
|
menu = inject(RiviniaMenuComponent, {
|
|
42
44
|
optional: true,
|
|
43
45
|
skipSelf: true
|
|
@@ -53,20 +55,41 @@ var RiviniaMenuItem = class RiviniaMenuItem {
|
|
|
53
55
|
const checked = this.checked();
|
|
54
56
|
return checked === null ? null : String(checked);
|
|
55
57
|
}
|
|
58
|
+
isDisabled() {
|
|
59
|
+
if (this.disabled()) return true;
|
|
60
|
+
return this.host?.nativeElement?.disabled === true;
|
|
61
|
+
}
|
|
56
62
|
pick() {
|
|
57
63
|
this.menu?.pickItem(this.id());
|
|
58
64
|
}
|
|
59
65
|
hover() {
|
|
60
66
|
this.menu?.hoverItem(this.id());
|
|
61
67
|
}
|
|
68
|
+
registeredId = null;
|
|
69
|
+
constructor() {
|
|
70
|
+
effect(() => {
|
|
71
|
+
const id = this.id();
|
|
72
|
+
const menu = this.menu;
|
|
73
|
+
if (this.registeredId === null) return;
|
|
74
|
+
if (this.registeredId !== id) menu?.unregisterItem(this.registeredId);
|
|
75
|
+
menu?.registerItem({
|
|
76
|
+
id,
|
|
77
|
+
disabled: this.isDisabled()
|
|
78
|
+
});
|
|
79
|
+
this.registeredId = id;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
62
82
|
ngOnInit() {
|
|
83
|
+
this.registeredId = this.id();
|
|
63
84
|
this.menu?.registerItem({
|
|
64
85
|
id: this.id(),
|
|
65
|
-
disabled: this.
|
|
86
|
+
disabled: this.isDisabled()
|
|
66
87
|
});
|
|
67
88
|
}
|
|
68
89
|
ngOnDestroy() {
|
|
69
|
-
|
|
90
|
+
if (this.registeredId === null) return;
|
|
91
|
+
this.menu?.unregisterItem(this.registeredId);
|
|
92
|
+
this.registeredId = null;
|
|
70
93
|
}
|
|
71
94
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
72
95
|
minVersion: "12.0.0",
|
|
@@ -114,7 +137,7 @@ var RiviniaMenuItem = class RiviniaMenuItem {
|
|
|
114
137
|
"attr.role": "computedRole()",
|
|
115
138
|
"attr.data-rv-id": "id()",
|
|
116
139
|
"attr.tabindex": "tab()",
|
|
117
|
-
"attr.aria-disabled": "
|
|
140
|
+
"attr.aria-disabled": "isDisabled()",
|
|
118
141
|
"attr.data-active": "active()",
|
|
119
142
|
"attr.aria-checked": "checkedAttr()"
|
|
120
143
|
}
|
|
@@ -136,7 +159,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
136
159
|
"[attr.role]": "computedRole()",
|
|
137
160
|
"[attr.data-rv-id]": "id()",
|
|
138
161
|
"[attr.tabindex]": "tab()",
|
|
139
|
-
"[attr.aria-disabled]": "
|
|
162
|
+
"[attr.aria-disabled]": "isDisabled()",
|
|
140
163
|
"[attr.data-active]": "active()",
|
|
141
164
|
"[attr.aria-checked]": "checkedAttr()",
|
|
142
165
|
"(click)": "pick()",
|
|
@@ -144,6 +167,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
144
167
|
}
|
|
145
168
|
}]
|
|
146
169
|
}],
|
|
170
|
+
ctorParameters: () => [],
|
|
147
171
|
propDecorators: {
|
|
148
172
|
id: [{
|
|
149
173
|
type: i0.Input,
|
|
@@ -174,12 +198,22 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
174
198
|
var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
175
199
|
menu = input(null, ...ngDevMode ? [{ debugName: "menu" }] : /* istanbul ignore next */ []);
|
|
176
200
|
placement = input("bottom-start", ...ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []);
|
|
201
|
+
menuLabel = input(null, ...ngDevMode ? [{ debugName: "menuLabel" }] : /* istanbul ignore next */ []);
|
|
202
|
+
items = input(null, ...ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []);
|
|
203
|
+
panelClass = input(null, ...ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []);
|
|
204
|
+
itemClass = input(null, ...ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []);
|
|
177
205
|
picked = output();
|
|
178
206
|
opened = output();
|
|
179
207
|
closed = output();
|
|
180
208
|
owned = new RiviniaMenu();
|
|
181
209
|
self = inject(ElementRef);
|
|
210
|
+
projectedItems = contentChildren(RiviniaMenuItem, {
|
|
211
|
+
...ngDevMode ? { debugName: "projectedItems" } : /* istanbul ignore next */ {},
|
|
212
|
+
descendants: true
|
|
213
|
+
});
|
|
214
|
+
panelId = `rv-menu-panel-${++nextPanelId}`;
|
|
182
215
|
focusPending = false;
|
|
216
|
+
pendingActivation = false;
|
|
183
217
|
topLayerSupported = typeof HTMLElement !== "undefined" && "showPopover" in HTMLElement.prototype;
|
|
184
218
|
positioned = signal(false, ...ngDevMode ? [{ debugName: "positioned" }] : /* istanbul ignore next */ []);
|
|
185
219
|
flipped = signal(false, ...ngDevMode ? [{ debugName: "flipped" }] : /* istanbul ignore next */ []);
|
|
@@ -188,19 +222,38 @@ var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
|
188
222
|
controller() {
|
|
189
223
|
return this.menu() ?? this.owned;
|
|
190
224
|
}
|
|
225
|
+
dataItems() {
|
|
226
|
+
return this.ambiguous() ? [] : this.items() ?? [];
|
|
227
|
+
}
|
|
228
|
+
checkedFor(id) {
|
|
229
|
+
const c = this.controller();
|
|
230
|
+
return typeof c.isSelected === "function" ? c.isSelected(id) : null;
|
|
231
|
+
}
|
|
232
|
+
ambiguous() {
|
|
233
|
+
return this.items() !== null && this.projectedItems().length > 0;
|
|
234
|
+
}
|
|
191
235
|
registerItem(item) {
|
|
192
|
-
this.controller()
|
|
236
|
+
const c = this.controller();
|
|
237
|
+
c.register(item);
|
|
238
|
+
if (this.pendingActivation && c.open() && c.activeId() === null && !item.disabled) {
|
|
239
|
+
c.activateFirst();
|
|
240
|
+
this.pendingActivation = false;
|
|
241
|
+
}
|
|
193
242
|
}
|
|
194
243
|
unregisterItem(id) {
|
|
195
244
|
this.controller().unregister(id);
|
|
196
245
|
}
|
|
197
246
|
constructor() {
|
|
247
|
+
effect(() => {
|
|
248
|
+
if (!this.ambiguous()) return;
|
|
249
|
+
throw new Error("rv-menu: `[items]` cannot be combined with projected `rvMenuItem` children — pick one way to populate the panel.");
|
|
250
|
+
});
|
|
198
251
|
effect((onCleanup) => {
|
|
199
252
|
if (typeof window === "undefined") return;
|
|
200
253
|
if (!this.controller().open()) return;
|
|
201
254
|
const onPointer = (event) => {
|
|
202
255
|
const host = this.self.nativeElement;
|
|
203
|
-
if (host && !host.contains(event.target)) this.doClose(
|
|
256
|
+
if (host && !host.contains(event.target)) this.doClose(false);
|
|
204
257
|
};
|
|
205
258
|
document.addEventListener("pointerdown", onPointer);
|
|
206
259
|
const onScroll = (event) => {
|
|
@@ -222,6 +275,7 @@ var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
|
222
275
|
const trigger = this.triggerEl();
|
|
223
276
|
trigger?.setAttribute("aria-haspopup", "menu");
|
|
224
277
|
trigger?.setAttribute("aria-expanded", String(open));
|
|
278
|
+
trigger?.setAttribute("aria-controls", this.panelId);
|
|
225
279
|
});
|
|
226
280
|
afterRenderEffect(() => {
|
|
227
281
|
if (this.controller().open() && !this.positioned()) this.showAndPosition();
|
|
@@ -279,10 +333,9 @@ var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
|
279
333
|
}
|
|
280
334
|
markOpening() {
|
|
281
335
|
this.focusPending = true;
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
336
|
+
this.pendingActivation = true;
|
|
337
|
+
this.flipped.set(false);
|
|
338
|
+
this.positioned.set(false);
|
|
286
339
|
}
|
|
287
340
|
onTriggerClick() {
|
|
288
341
|
const c = this.controller();
|
|
@@ -347,12 +400,23 @@ var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
|
347
400
|
event.preventDefault();
|
|
348
401
|
this.pickFocused();
|
|
349
402
|
break;
|
|
350
|
-
case "Tab":
|
|
403
|
+
case "Tab": {
|
|
404
|
+
event.preventDefault();
|
|
405
|
+
const backwards = event.shiftKey;
|
|
351
406
|
this.doClose(false);
|
|
407
|
+
this.focusAdjacentToTrigger(backwards);
|
|
352
408
|
break;
|
|
409
|
+
}
|
|
353
410
|
default: this.onTypeahead(event);
|
|
354
411
|
}
|
|
355
412
|
}
|
|
413
|
+
resetTypeahead() {
|
|
414
|
+
this.typeBuffer = "";
|
|
415
|
+
if (this.typeTimer !== null) {
|
|
416
|
+
clearTimeout(this.typeTimer);
|
|
417
|
+
this.typeTimer = null;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
356
420
|
onTypeahead(event) {
|
|
357
421
|
if (event.key.length !== 1 || event.ctrlKey || event.metaKey || event.altKey) return;
|
|
358
422
|
const ch = event.key.toLocaleLowerCase();
|
|
@@ -381,8 +445,11 @@ var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
|
381
445
|
return rows[0].id;
|
|
382
446
|
}
|
|
383
447
|
pickFocused() {
|
|
384
|
-
const
|
|
448
|
+
const c = this.controller();
|
|
449
|
+
const id = c.pickActive();
|
|
385
450
|
if (id === null) return;
|
|
451
|
+
const item = c.items().find((i) => i.id === id);
|
|
452
|
+
if (!item || item.disabled) return;
|
|
386
453
|
this.doClose(false);
|
|
387
454
|
this.picked.emit({ id });
|
|
388
455
|
}
|
|
@@ -396,16 +463,22 @@ var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
|
396
463
|
}
|
|
397
464
|
doClose(restoreFocus) {
|
|
398
465
|
const was = this.controller().open();
|
|
466
|
+
this.resetTypeahead();
|
|
399
467
|
this.controller().closeMenu();
|
|
400
468
|
if (was) this.closed.emit();
|
|
401
469
|
if (restoreFocus) this.focusTrigger();
|
|
402
470
|
}
|
|
403
471
|
consumePendingFocus() {
|
|
404
|
-
if (!this.controller().open() || !this.focusPending) return;
|
|
405
|
-
this.focusPending = false;
|
|
472
|
+
if (!this.positioned() || !this.controller().open() || !this.focusPending) return;
|
|
406
473
|
const root = this.self.nativeElement.querySelector(".rv-menu-panel");
|
|
474
|
+
if (!root) return;
|
|
407
475
|
const active = this.activeDocumentElement();
|
|
408
|
-
if (
|
|
476
|
+
if (active && root.contains(active)) {
|
|
477
|
+
this.focusPending = false;
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
this.focusPending = false;
|
|
481
|
+
this.focusActive();
|
|
409
482
|
}
|
|
410
483
|
focusActive() {
|
|
411
484
|
const id = this.controller().activeId();
|
|
@@ -428,6 +501,22 @@ var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
|
428
501
|
if (!wrap) return null;
|
|
429
502
|
return wrap.querySelector("button, a, [tabindex]") ?? wrap;
|
|
430
503
|
}
|
|
504
|
+
focusAdjacentToTrigger(backwards) {
|
|
505
|
+
try {
|
|
506
|
+
const trigger = this.triggerEl();
|
|
507
|
+
const doc = trigger?.ownerDocument;
|
|
508
|
+
if (!trigger || !doc) return;
|
|
509
|
+
const stops = [...doc.querySelectorAll("a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])")].filter((el) => {
|
|
510
|
+
if (el.hasAttribute("disabled") || el.getAttribute("aria-hidden") === "true") return false;
|
|
511
|
+
if (el.closest(".rv-menu-panel")) return false;
|
|
512
|
+
return el.getClientRects().length > 0;
|
|
513
|
+
});
|
|
514
|
+
stops.sort((a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
|
|
515
|
+
const at = stops.indexOf(trigger);
|
|
516
|
+
if (at === -1) return;
|
|
517
|
+
stops[backwards ? at - 1 : at + 1]?.focus({ preventScroll: true });
|
|
518
|
+
} catch {}
|
|
519
|
+
}
|
|
431
520
|
focusTrigger() {
|
|
432
521
|
try {
|
|
433
522
|
this.triggerEl()?.focus({ preventScroll: true });
|
|
@@ -461,6 +550,34 @@ var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
|
461
550
|
isSignal: true,
|
|
462
551
|
isRequired: false,
|
|
463
552
|
transformFunction: null
|
|
553
|
+
},
|
|
554
|
+
menuLabel: {
|
|
555
|
+
classPropertyName: "menuLabel",
|
|
556
|
+
publicName: "menuLabel",
|
|
557
|
+
isSignal: true,
|
|
558
|
+
isRequired: false,
|
|
559
|
+
transformFunction: null
|
|
560
|
+
},
|
|
561
|
+
items: {
|
|
562
|
+
classPropertyName: "items",
|
|
563
|
+
publicName: "items",
|
|
564
|
+
isSignal: true,
|
|
565
|
+
isRequired: false,
|
|
566
|
+
transformFunction: null
|
|
567
|
+
},
|
|
568
|
+
panelClass: {
|
|
569
|
+
classPropertyName: "panelClass",
|
|
570
|
+
publicName: "panelClass",
|
|
571
|
+
isSignal: true,
|
|
572
|
+
isRequired: false,
|
|
573
|
+
transformFunction: null
|
|
574
|
+
},
|
|
575
|
+
itemClass: {
|
|
576
|
+
classPropertyName: "itemClass",
|
|
577
|
+
publicName: "itemClass",
|
|
578
|
+
isSignal: true,
|
|
579
|
+
isRequired: false,
|
|
580
|
+
transformFunction: null
|
|
464
581
|
}
|
|
465
582
|
},
|
|
466
583
|
outputs: {
|
|
@@ -468,16 +585,25 @@ var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
|
468
585
|
opened: "opened",
|
|
469
586
|
closed: "closed"
|
|
470
587
|
},
|
|
588
|
+
queries: [{
|
|
589
|
+
propertyName: "projectedItems",
|
|
590
|
+
predicate: RiviniaMenuItem,
|
|
591
|
+
descendants: true,
|
|
592
|
+
isSignal: true
|
|
593
|
+
}],
|
|
471
594
|
ngImport: i0,
|
|
472
595
|
template: `
|
|
473
596
|
<div class="rv-menu-trigger" (click)="onTriggerClick()" (keydown)="onTriggerKey($event)">
|
|
474
|
-
<ng-content select="[rv-menu-trigger]"></ng-content>
|
|
597
|
+
<ng-content select="[rv-menu-trigger], [rvMenuTrigger]"></ng-content>
|
|
475
598
|
</div>
|
|
476
599
|
@if (controller().open()) {
|
|
477
600
|
<div
|
|
478
601
|
#panel
|
|
479
602
|
class="rv-menu-panel"
|
|
603
|
+
[class]="panelClass()"
|
|
480
604
|
role="menu"
|
|
605
|
+
[attr.id]="panelId"
|
|
606
|
+
[attr.aria-label]="menuLabel()"
|
|
481
607
|
[attr.popover]="topLayerSupported ? 'manual' : null"
|
|
482
608
|
[attr.data-placement]="placement()"
|
|
483
609
|
[attr.data-flipped]="flipped()"
|
|
@@ -485,12 +611,35 @@ var RiviniaMenuComponent = class RiviniaMenuComponent {
|
|
|
485
611
|
[attr.data-top-layer]="topLayerSupported"
|
|
486
612
|
(keydown)="onPanelKey($event)"
|
|
487
613
|
>
|
|
488
|
-
|
|
614
|
+
@if (dataItems().length > 0) {
|
|
615
|
+
@for (item of dataItems(); track item.id) {
|
|
616
|
+
<button
|
|
617
|
+
rvMenuItem
|
|
618
|
+
[id]="item.id"
|
|
619
|
+
[disabled]="item.disabled ?? false"
|
|
620
|
+
[checked]="checkedFor(item.id)"
|
|
621
|
+
[class]="itemClass()"
|
|
622
|
+
>
|
|
623
|
+
{{ item.label }}
|
|
624
|
+
</button>
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
<ng-content select="[rv-menu-panel], [rvMenuPanel]"></ng-content>
|
|
489
628
|
</div>
|
|
490
629
|
}
|
|
491
630
|
`,
|
|
492
631
|
isInline: true,
|
|
493
|
-
styles: [".rv-menu-trigger{display:inline-flex;align-items:center}.rv-menu-panel{display:flex;flex-direction:column;gap:2px}.rv-menu-panel hr{border:0;border-top:1px solid var(--rv-menu-border);margin:6px 4px}rv-menu{position:relative;display:inline-block}.rv-menu-panel[data-top-layer=true]{position:fixed;inset:auto;margin:0}.rv-menu-panel[data-positioned=false]{animation:none}.rv-menu-panel[data-top-layer=true][data-placement^=bottom][data-flipped=true]{transform-origin:bottom center}.rv-menu-panel[data-top-layer=true][data-placement^=top][data-flipped=true]{transform-origin:top center}.rv-menu-panel{
|
|
632
|
+
styles: [".rv-menu-trigger{display:inline-flex;align-items:center}.rv-menu-panel{display:flex;flex-direction:column;gap:2px}.rv-menu-panel hr{border:0;border-top:1px solid var(--rv-menu-border, #e0e0e0);margin:6px 4px}rv-menu{position:relative;display:inline-block}.rv-menu-panel[data-top-layer=true]{position:fixed;inset:auto;margin:0}.rv-menu-panel[data-positioned=false]{animation:none}.rv-menu-panel[data-top-layer=true][data-placement^=bottom][data-flipped=true]{transform-origin:bottom center}.rv-menu-panel[data-top-layer=true][data-placement^=top][data-flipped=true]{transform-origin:top center}.rv-menu-panel{position:absolute;z-index:var(--rv-menu-z, 500);min-width:var(--rv-menu-width, 16rem);max-width:calc(100dvw - 2rem);max-height:min(20rem,calc(100dvh - 2rem));overflow-y:auto;background:var(--rv-menu-bg, #ffffff);color:var(--rv-menu-fg, #111111);font-family:var(--rv-menu-font, system-ui, -apple-system, \"Segoe UI\", sans-serif);border:1px solid var(--rv-menu-border, #e0e0e0);border-radius:var(--rv-menu-radius, 12px);box-shadow:var(--rv-menu-shadow, 0 16px 48px rgba(0, 0, 0, .22));padding:var(--rv-menu-pad, 6px)}.rv-menu-panel[data-placement=bottom-start]{inset-block-start:calc(100% + 6px);inset-inline-start:0}.rv-menu-panel[data-placement=bottom-end]{inset-block-start:calc(100% + 6px);inset-inline-end:0}.rv-menu-panel[data-placement=top-start]{inset-block-end:calc(100% + 6px);inset-inline-start:0}.rv-menu-panel[data-placement=top-end]{inset-block-end:calc(100% + 6px);inset-inline-end:0}.rv-menu-panel [rvmenuitem]{display:flex;align-items:center;gap:10px;width:100%;min-height:40px;border:0;border-radius:8px;background:transparent;color:inherit;font:inherit;font-size:14px;line-height:1.4;text-align:start;padding:8px 10px;cursor:pointer}.rv-menu-panel [rvmenuitem][data-active=true]{background:color-mix(in srgb,currentColor 10%,transparent)}.rv-menu-panel [rvmenuitem]:hover{background:color-mix(in srgb,currentColor 8%,transparent)}.rv-menu-panel [rvmenuitem]:focus-visible{outline:2px solid currentColor;outline-offset:-2px}.rv-menu-panel [rvmenuitem][aria-disabled=true]{opacity:.45;cursor:default}.rv-menu-panel [rvmenuitem] svg,.rv-menu-panel [rvmenuitem] [data-icon]{width:16px;height:16px;flex:none;color:var(--rv-menu-muted)}.rv-menu-panel [rvmenuitem][data-active=true] svg,.rv-menu-panel [rvmenuitem][data-active=true] [data-icon]{color:inherit}.rv-menu-panel{transform-origin:top center;animation:rv-menu-in var(--rv-menu-duration, .16s) var(--rv-menu-easing, cubic-bezier(.16, 1, .3, 1))}.rv-menu-panel[data-placement=top-start],.rv-menu-panel[data-placement=top-end]{--rv-menu-from-y: 6px;transform-origin:bottom center}@media(prefers-reduced-motion:reduce){.rv-menu-panel{animation:none}}@keyframes rv-menu-in{0%{opacity:0;transform:translateY(var(--rv-menu-from-y, -6px)) scale(.96)}}\n"],
|
|
633
|
+
dependencies: [{
|
|
634
|
+
kind: "directive",
|
|
635
|
+
type: RiviniaMenuItem,
|
|
636
|
+
selector: "[rvMenuItem]",
|
|
637
|
+
inputs: [
|
|
638
|
+
"id",
|
|
639
|
+
"disabled",
|
|
640
|
+
"checked"
|
|
641
|
+
]
|
|
642
|
+
}],
|
|
494
643
|
encapsulation: i0.ViewEncapsulation.None
|
|
495
644
|
});
|
|
496
645
|
};
|
|
@@ -505,15 +654,19 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
505
654
|
selector: "rv-menu",
|
|
506
655
|
standalone: true,
|
|
507
656
|
encapsulation: ViewEncapsulation.None,
|
|
657
|
+
imports: [RiviniaMenuItem],
|
|
508
658
|
template: `
|
|
509
659
|
<div class="rv-menu-trigger" (click)="onTriggerClick()" (keydown)="onTriggerKey($event)">
|
|
510
|
-
<ng-content select="[rv-menu-trigger]"></ng-content>
|
|
660
|
+
<ng-content select="[rv-menu-trigger], [rvMenuTrigger]"></ng-content>
|
|
511
661
|
</div>
|
|
512
662
|
@if (controller().open()) {
|
|
513
663
|
<div
|
|
514
664
|
#panel
|
|
515
665
|
class="rv-menu-panel"
|
|
666
|
+
[class]="panelClass()"
|
|
516
667
|
role="menu"
|
|
668
|
+
[attr.id]="panelId"
|
|
669
|
+
[attr.aria-label]="menuLabel()"
|
|
517
670
|
[attr.popover]="topLayerSupported ? 'manual' : null"
|
|
518
671
|
[attr.data-placement]="placement()"
|
|
519
672
|
[attr.data-flipped]="flipped()"
|
|
@@ -521,11 +674,24 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
521
674
|
[attr.data-top-layer]="topLayerSupported"
|
|
522
675
|
(keydown)="onPanelKey($event)"
|
|
523
676
|
>
|
|
524
|
-
|
|
677
|
+
@if (dataItems().length > 0) {
|
|
678
|
+
@for (item of dataItems(); track item.id) {
|
|
679
|
+
<button
|
|
680
|
+
rvMenuItem
|
|
681
|
+
[id]="item.id"
|
|
682
|
+
[disabled]="item.disabled ?? false"
|
|
683
|
+
[checked]="checkedFor(item.id)"
|
|
684
|
+
[class]="itemClass()"
|
|
685
|
+
>
|
|
686
|
+
{{ item.label }}
|
|
687
|
+
</button>
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
<ng-content select="[rv-menu-panel], [rvMenuPanel]"></ng-content>
|
|
525
691
|
</div>
|
|
526
692
|
}
|
|
527
693
|
`,
|
|
528
|
-
styles: [".rv-menu-trigger{display:inline-flex;align-items:center}.rv-menu-panel{display:flex;flex-direction:column;gap:2px}.rv-menu-panel hr{border:0;border-top:1px solid var(--rv-menu-border);margin:6px 4px}rv-menu{position:relative;display:inline-block}.rv-menu-panel[data-top-layer=true]{position:fixed;inset:auto;margin:0}.rv-menu-panel[data-positioned=false]{animation:none}.rv-menu-panel[data-top-layer=true][data-placement^=bottom][data-flipped=true]{transform-origin:bottom center}.rv-menu-panel[data-top-layer=true][data-placement^=top][data-flipped=true]{transform-origin:top center}.rv-menu-panel{
|
|
694
|
+
styles: [".rv-menu-trigger{display:inline-flex;align-items:center}.rv-menu-panel{display:flex;flex-direction:column;gap:2px}.rv-menu-panel hr{border:0;border-top:1px solid var(--rv-menu-border, #e0e0e0);margin:6px 4px}rv-menu{position:relative;display:inline-block}.rv-menu-panel[data-top-layer=true]{position:fixed;inset:auto;margin:0}.rv-menu-panel[data-positioned=false]{animation:none}.rv-menu-panel[data-top-layer=true][data-placement^=bottom][data-flipped=true]{transform-origin:bottom center}.rv-menu-panel[data-top-layer=true][data-placement^=top][data-flipped=true]{transform-origin:top center}.rv-menu-panel{position:absolute;z-index:var(--rv-menu-z, 500);min-width:var(--rv-menu-width, 16rem);max-width:calc(100dvw - 2rem);max-height:min(20rem,calc(100dvh - 2rem));overflow-y:auto;background:var(--rv-menu-bg, #ffffff);color:var(--rv-menu-fg, #111111);font-family:var(--rv-menu-font, system-ui, -apple-system, \"Segoe UI\", sans-serif);border:1px solid var(--rv-menu-border, #e0e0e0);border-radius:var(--rv-menu-radius, 12px);box-shadow:var(--rv-menu-shadow, 0 16px 48px rgba(0, 0, 0, .22));padding:var(--rv-menu-pad, 6px)}.rv-menu-panel[data-placement=bottom-start]{inset-block-start:calc(100% + 6px);inset-inline-start:0}.rv-menu-panel[data-placement=bottom-end]{inset-block-start:calc(100% + 6px);inset-inline-end:0}.rv-menu-panel[data-placement=top-start]{inset-block-end:calc(100% + 6px);inset-inline-start:0}.rv-menu-panel[data-placement=top-end]{inset-block-end:calc(100% + 6px);inset-inline-end:0}.rv-menu-panel [rvmenuitem]{display:flex;align-items:center;gap:10px;width:100%;min-height:40px;border:0;border-radius:8px;background:transparent;color:inherit;font:inherit;font-size:14px;line-height:1.4;text-align:start;padding:8px 10px;cursor:pointer}.rv-menu-panel [rvmenuitem][data-active=true]{background:color-mix(in srgb,currentColor 10%,transparent)}.rv-menu-panel [rvmenuitem]:hover{background:color-mix(in srgb,currentColor 8%,transparent)}.rv-menu-panel [rvmenuitem]:focus-visible{outline:2px solid currentColor;outline-offset:-2px}.rv-menu-panel [rvmenuitem][aria-disabled=true]{opacity:.45;cursor:default}.rv-menu-panel [rvmenuitem] svg,.rv-menu-panel [rvmenuitem] [data-icon]{width:16px;height:16px;flex:none;color:var(--rv-menu-muted)}.rv-menu-panel [rvmenuitem][data-active=true] svg,.rv-menu-panel [rvmenuitem][data-active=true] [data-icon]{color:inherit}.rv-menu-panel{transform-origin:top center;animation:rv-menu-in var(--rv-menu-duration, .16s) var(--rv-menu-easing, cubic-bezier(.16, 1, .3, 1))}.rv-menu-panel[data-placement=top-start],.rv-menu-panel[data-placement=top-end]{--rv-menu-from-y: 6px;transform-origin:bottom center}@media(prefers-reduced-motion:reduce){.rv-menu-panel{animation:none}}@keyframes rv-menu-in{0%{opacity:0;transform:translateY(var(--rv-menu-from-y, -6px)) scale(.96)}}\n"]
|
|
529
695
|
}]
|
|
530
696
|
}],
|
|
531
697
|
ctorParameters: () => [],
|
|
@@ -546,6 +712,38 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
546
712
|
required: false
|
|
547
713
|
}]
|
|
548
714
|
}],
|
|
715
|
+
menuLabel: [{
|
|
716
|
+
type: i0.Input,
|
|
717
|
+
args: [{
|
|
718
|
+
isSignal: true,
|
|
719
|
+
alias: "menuLabel",
|
|
720
|
+
required: false
|
|
721
|
+
}]
|
|
722
|
+
}],
|
|
723
|
+
items: [{
|
|
724
|
+
type: i0.Input,
|
|
725
|
+
args: [{
|
|
726
|
+
isSignal: true,
|
|
727
|
+
alias: "items",
|
|
728
|
+
required: false
|
|
729
|
+
}]
|
|
730
|
+
}],
|
|
731
|
+
panelClass: [{
|
|
732
|
+
type: i0.Input,
|
|
733
|
+
args: [{
|
|
734
|
+
isSignal: true,
|
|
735
|
+
alias: "panelClass",
|
|
736
|
+
required: false
|
|
737
|
+
}]
|
|
738
|
+
}],
|
|
739
|
+
itemClass: [{
|
|
740
|
+
type: i0.Input,
|
|
741
|
+
args: [{
|
|
742
|
+
isSignal: true,
|
|
743
|
+
alias: "itemClass",
|
|
744
|
+
required: false
|
|
745
|
+
}]
|
|
746
|
+
}],
|
|
549
747
|
picked: [{
|
|
550
748
|
type: i0.Output,
|
|
551
749
|
args: ["picked"]
|
|
@@ -557,6 +755,13 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
557
755
|
closed: [{
|
|
558
756
|
type: i0.Output,
|
|
559
757
|
args: ["closed"]
|
|
758
|
+
}],
|
|
759
|
+
projectedItems: [{
|
|
760
|
+
type: i0.ContentChildren,
|
|
761
|
+
args: [i0.forwardRef(() => RiviniaMenuItem), {
|
|
762
|
+
descendants: true,
|
|
763
|
+
isSignal: true
|
|
764
|
+
}]
|
|
560
765
|
}]
|
|
561
766
|
}
|
|
562
767
|
});
|