cerxos-ui 0.0.8 → 0.0.10
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/fesm2022/cerxos-ui.mjs +66 -4
- package/fesm2022/cerxos-ui.mjs.map +1 -1
- package/index.d.ts +9 -1
- package/package.json +1 -1
package/fesm2022/cerxos-ui.mjs
CHANGED
|
@@ -27,9 +27,11 @@ const TRIGGER_BASE_CLASSES = 'inline-flex items-center justify-center rounded-[v
|
|
|
27
27
|
'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
|
|
28
28
|
'focus-visible:outline-[var(--cxs-color-focus)]';
|
|
29
29
|
const TRIGGER_DISABLED_CLASSES = 'pointer-events-none opacity-60';
|
|
30
|
-
const MENU_BASE_CLASSES = '
|
|
30
|
+
const MENU_BASE_CLASSES = 'min-w-[10rem] rounded-[var(--cxs-radius-md)] ' +
|
|
31
31
|
'border border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] py-1 ' +
|
|
32
32
|
'shadow-[var(--cxs-shadow-sm)]';
|
|
33
|
+
const MENU_POSITION_CLASSES = 'absolute z-40 mt-2';
|
|
34
|
+
const MENU_FLOATING_CLASSES = 'fixed z-50';
|
|
33
35
|
const MENU_ALIGN_CLASSES = {
|
|
34
36
|
start: 'left-0 origin-top-left',
|
|
35
37
|
end: 'right-0 origin-top-right'
|
|
@@ -49,11 +51,14 @@ class CxsActionMenuComponent {
|
|
|
49
51
|
ariaLabel = 'Open actions menu';
|
|
50
52
|
menuLabel = 'Actions';
|
|
51
53
|
disabled = false;
|
|
54
|
+
floating = false;
|
|
52
55
|
itemSelected = new EventEmitter();
|
|
53
56
|
trigger;
|
|
57
|
+
menu;
|
|
54
58
|
menuItems;
|
|
55
59
|
menuId = `cxs-action-menu-${CxsActionMenuComponent.nextId++}`;
|
|
56
60
|
isOpen = false;
|
|
61
|
+
menuPosition = null;
|
|
57
62
|
constructor(elementRef) {
|
|
58
63
|
this.elementRef = elementRef;
|
|
59
64
|
}
|
|
@@ -61,7 +66,13 @@ class CxsActionMenuComponent {
|
|
|
61
66
|
return [TRIGGER_BASE_CLASSES, this.disabled ? TRIGGER_DISABLED_CLASSES : ''].filter(Boolean).join(' ');
|
|
62
67
|
}
|
|
63
68
|
get menuClass() {
|
|
64
|
-
return [
|
|
69
|
+
return [
|
|
70
|
+
MENU_BASE_CLASSES,
|
|
71
|
+
this.floating ? MENU_FLOATING_CLASSES : MENU_POSITION_CLASSES,
|
|
72
|
+
!this.floating ? MENU_ALIGN_CLASSES[this.align] : ''
|
|
73
|
+
]
|
|
74
|
+
.filter(Boolean)
|
|
75
|
+
.join(' ');
|
|
65
76
|
}
|
|
66
77
|
get emptyStateClass() {
|
|
67
78
|
return EMPTY_STATE_CLASSES$1;
|
|
@@ -91,10 +102,12 @@ class CxsActionMenuComponent {
|
|
|
91
102
|
return;
|
|
92
103
|
}
|
|
93
104
|
this.isOpen = true;
|
|
105
|
+
this.updateMenuPosition();
|
|
94
106
|
this.focusFirstItem();
|
|
95
107
|
}
|
|
96
108
|
close() {
|
|
97
109
|
this.isOpen = false;
|
|
110
|
+
this.menuPosition = null;
|
|
98
111
|
}
|
|
99
112
|
onTriggerKeydown(event) {
|
|
100
113
|
if (this.disabled) {
|
|
@@ -162,6 +175,12 @@ class CxsActionMenuComponent {
|
|
|
162
175
|
this.close();
|
|
163
176
|
}
|
|
164
177
|
}
|
|
178
|
+
onWindowResize() {
|
|
179
|
+
if (!this.isOpen) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this.updateMenuPosition();
|
|
183
|
+
}
|
|
165
184
|
focusFirstItem() {
|
|
166
185
|
if (typeof document === 'undefined') {
|
|
167
186
|
return;
|
|
@@ -174,17 +193,52 @@ class CxsActionMenuComponent {
|
|
|
174
193
|
focusTrigger() {
|
|
175
194
|
this.trigger?.nativeElement.focus();
|
|
176
195
|
}
|
|
196
|
+
updateMenuPosition() {
|
|
197
|
+
if (!this.floating) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (typeof window === 'undefined') {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const trigger = this.trigger?.nativeElement;
|
|
204
|
+
if (!trigger) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const rect = trigger.getBoundingClientRect();
|
|
208
|
+
const offset = 8;
|
|
209
|
+
setTimeout(() => {
|
|
210
|
+
const menuEl = this.menu?.nativeElement;
|
|
211
|
+
const menuRect = menuEl?.getBoundingClientRect();
|
|
212
|
+
const menuWidth = menuRect?.width ?? 160;
|
|
213
|
+
const menuHeight = menuRect?.height ?? 0;
|
|
214
|
+
let top = rect.bottom + offset;
|
|
215
|
+
let left = this.align === 'end' ? rect.right - menuWidth : rect.left;
|
|
216
|
+
if (top + menuHeight > window.innerHeight) {
|
|
217
|
+
top = rect.top - menuHeight - offset;
|
|
218
|
+
}
|
|
219
|
+
if (top < offset) {
|
|
220
|
+
top = offset;
|
|
221
|
+
}
|
|
222
|
+
if (left + menuWidth > window.innerWidth - offset) {
|
|
223
|
+
left = Math.max(offset, window.innerWidth - menuWidth - offset);
|
|
224
|
+
}
|
|
225
|
+
if (left < offset) {
|
|
226
|
+
left = offset;
|
|
227
|
+
}
|
|
228
|
+
this.menuPosition = { top, left };
|
|
229
|
+
}, 0);
|
|
230
|
+
}
|
|
177
231
|
getFocusableItems() {
|
|
178
232
|
return (this.menuItems?.toArray() ?? [])
|
|
179
233
|
.map((item) => item.nativeElement)
|
|
180
234
|
.filter((item) => !item.hasAttribute('disabled') && !item.hasAttribute('data-disabled'));
|
|
181
235
|
}
|
|
182
236
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsActionMenuComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
183
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsActionMenuComponent, isStandalone: true, selector: "cxs-action-menu", inputs: { items: "items", align: "align", ariaLabel: "ariaLabel", menuLabel: "menuLabel", disabled: "disabled" }, outputs: { itemSelected: "itemSelected" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true, read: ElementRef }, { propertyName: "menuItems", predicate: ["menuItem"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"relative inline-flex\">\n <button\n #trigger\n type=\"button\"\n [class]=\"triggerClass\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"menuId\"\n [attr.aria-label]=\"ariaLabel\"\n (click)=\"toggle()\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n <svg class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"4\" cy=\"10\" r=\"1.5\"></circle>\n <circle cx=\"10\" cy=\"10\" r=\"1.5\"></circle>\n <circle cx=\"16\" cy=\"10\" r=\"1.5\"></circle>\n </svg>\n </button>\n\n @if (isOpen) {\n <div\n [class]=\"menuClass\"\n [attr.id]=\"menuId\"\n role=\"menu\"\n [attr.aria-label]=\"menuLabel\"\n (keydown)=\"onMenuKeydown($event)\"\n >\n @if (items.length) {\n @for (item of items; track item.value) {\n <button\n #menuItem\n type=\"button\"\n role=\"menuitem\"\n [class]=\"getItemClass(item)\"\n [attr.data-disabled]=\"isItemDisabled(item) ? '' : null\"\n [attr.aria-disabled]=\"isItemDisabled(item) ? 'true' : null\"\n [disabled]=\"isItemDisabled(item)\"\n (click)=\"onItemClick(item)\"\n >\n {{ item.label }}\n </button>\n }\n } @else {\n <span [class]=\"emptyStateClass\">No actions</span>\n }\n </div>\n }\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
237
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsActionMenuComponent, isStandalone: true, selector: "cxs-action-menu", inputs: { items: "items", align: "align", ariaLabel: "ariaLabel", menuLabel: "menuLabel", disabled: "disabled", floating: "floating" }, outputs: { itemSelected: "itemSelected" }, host: { listeners: { "document:click": "onDocumentClick($event)", "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true, read: ElementRef }, { propertyName: "menu", first: true, predicate: ["menu"], descendants: true, read: ElementRef }, { propertyName: "menuItems", predicate: ["menuItem"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"relative inline-flex\">\n <button\n #trigger\n type=\"button\"\n [class]=\"triggerClass\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"menuId\"\n [attr.aria-label]=\"ariaLabel\"\n (click)=\"toggle()\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n <svg class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"4\" cy=\"10\" r=\"1.5\"></circle>\n <circle cx=\"10\" cy=\"10\" r=\"1.5\"></circle>\n <circle cx=\"16\" cy=\"10\" r=\"1.5\"></circle>\n </svg>\n </button>\n\n @if (isOpen) {\n <div\n #menu\n [class]=\"menuClass\"\n [attr.id]=\"menuId\"\n role=\"menu\"\n [attr.aria-label]=\"menuLabel\"\n [style.top.px]=\"menuPosition?.top\"\n [style.left.px]=\"menuPosition?.left\"\n (keydown)=\"onMenuKeydown($event)\"\n >\n @if (items.length) {\n @for (item of items; track item.value) {\n <button\n #menuItem\n type=\"button\"\n role=\"menuitem\"\n [class]=\"getItemClass(item)\"\n [attr.data-disabled]=\"isItemDisabled(item) ? '' : null\"\n [attr.aria-disabled]=\"isItemDisabled(item) ? 'true' : null\"\n [disabled]=\"isItemDisabled(item)\"\n (click)=\"onItemClick(item)\"\n >\n {{ item.label }}\n </button>\n }\n } @else {\n <span [class]=\"emptyStateClass\">No actions</span>\n }\n </div>\n }\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
184
238
|
}
|
|
185
239
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsActionMenuComponent, decorators: [{
|
|
186
240
|
type: Component,
|
|
187
|
-
args: [{ selector: 'cxs-action-menu', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"relative inline-flex\">\n <button\n #trigger\n type=\"button\"\n [class]=\"triggerClass\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"menuId\"\n [attr.aria-label]=\"ariaLabel\"\n (click)=\"toggle()\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n <svg class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"4\" cy=\"10\" r=\"1.5\"></circle>\n <circle cx=\"10\" cy=\"10\" r=\"1.5\"></circle>\n <circle cx=\"16\" cy=\"10\" r=\"1.5\"></circle>\n </svg>\n </button>\n\n @if (isOpen) {\n <div\n [class]=\"menuClass\"\n [attr.id]=\"menuId\"\n role=\"menu\"\n [attr.aria-label]=\"menuLabel\"\n (keydown)=\"onMenuKeydown($event)\"\n >\n @if (items.length) {\n @for (item of items; track item.value) {\n <button\n #menuItem\n type=\"button\"\n role=\"menuitem\"\n [class]=\"getItemClass(item)\"\n [attr.data-disabled]=\"isItemDisabled(item) ? '' : null\"\n [attr.aria-disabled]=\"isItemDisabled(item) ? 'true' : null\"\n [disabled]=\"isItemDisabled(item)\"\n (click)=\"onItemClick(item)\"\n >\n {{ item.label }}\n </button>\n }\n } @else {\n <span [class]=\"emptyStateClass\">No actions</span>\n }\n </div>\n }\n</div>\n" }]
|
|
241
|
+
args: [{ selector: 'cxs-action-menu', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"relative inline-flex\">\n <button\n #trigger\n type=\"button\"\n [class]=\"triggerClass\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"menuId\"\n [attr.aria-label]=\"ariaLabel\"\n (click)=\"toggle()\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n <svg class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"4\" cy=\"10\" r=\"1.5\"></circle>\n <circle cx=\"10\" cy=\"10\" r=\"1.5\"></circle>\n <circle cx=\"16\" cy=\"10\" r=\"1.5\"></circle>\n </svg>\n </button>\n\n @if (isOpen) {\n <div\n #menu\n [class]=\"menuClass\"\n [attr.id]=\"menuId\"\n role=\"menu\"\n [attr.aria-label]=\"menuLabel\"\n [style.top.px]=\"menuPosition?.top\"\n [style.left.px]=\"menuPosition?.left\"\n (keydown)=\"onMenuKeydown($event)\"\n >\n @if (items.length) {\n @for (item of items; track item.value) {\n <button\n #menuItem\n type=\"button\"\n role=\"menuitem\"\n [class]=\"getItemClass(item)\"\n [attr.data-disabled]=\"isItemDisabled(item) ? '' : null\"\n [attr.aria-disabled]=\"isItemDisabled(item) ? 'true' : null\"\n [disabled]=\"isItemDisabled(item)\"\n (click)=\"onItemClick(item)\"\n >\n {{ item.label }}\n </button>\n }\n } @else {\n <span [class]=\"emptyStateClass\">No actions</span>\n }\n </div>\n }\n</div>\n" }]
|
|
188
242
|
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { items: [{
|
|
189
243
|
type: Input
|
|
190
244
|
}], align: [{
|
|
@@ -195,17 +249,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
195
249
|
type: Input
|
|
196
250
|
}], disabled: [{
|
|
197
251
|
type: Input
|
|
252
|
+
}], floating: [{
|
|
253
|
+
type: Input
|
|
198
254
|
}], itemSelected: [{
|
|
199
255
|
type: Output
|
|
200
256
|
}], trigger: [{
|
|
201
257
|
type: ViewChild,
|
|
202
258
|
args: ['trigger', { read: ElementRef }]
|
|
259
|
+
}], menu: [{
|
|
260
|
+
type: ViewChild,
|
|
261
|
+
args: ['menu', { read: ElementRef }]
|
|
203
262
|
}], menuItems: [{
|
|
204
263
|
type: ViewChildren,
|
|
205
264
|
args: ['menuItem', { read: ElementRef }]
|
|
206
265
|
}], onDocumentClick: [{
|
|
207
266
|
type: HostListener,
|
|
208
267
|
args: ['document:click', ['$event']]
|
|
268
|
+
}], onWindowResize: [{
|
|
269
|
+
type: HostListener,
|
|
270
|
+
args: ['window:resize']
|
|
209
271
|
}] } });
|
|
210
272
|
|
|
211
273
|
const BASE_CLASSES$a = 'relative flex w-full gap-3 rounded-[var(--cxs-radius-md)] border border-l-4 px-4 py-3 text-sm ' +
|