cerxos-ui 0.0.8 → 0.0.9
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 +71 -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,57 @@ 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
|
+
this.menuPosition = {
|
|
210
|
+
top: rect.bottom + offset,
|
|
211
|
+
left: rect.left
|
|
212
|
+
};
|
|
213
|
+
setTimeout(() => {
|
|
214
|
+
const menuEl = this.menu?.nativeElement;
|
|
215
|
+
const menuRect = menuEl?.getBoundingClientRect();
|
|
216
|
+
if (!menuRect) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
let top = rect.bottom + offset;
|
|
220
|
+
let left = this.align === 'end' ? rect.right - menuRect.width : rect.left;
|
|
221
|
+
if (top + menuRect.height > window.innerHeight) {
|
|
222
|
+
top = rect.top - menuRect.height - offset;
|
|
223
|
+
}
|
|
224
|
+
if (top < offset) {
|
|
225
|
+
top = offset;
|
|
226
|
+
}
|
|
227
|
+
if (left + menuRect.width > window.innerWidth - offset) {
|
|
228
|
+
left = Math.max(offset, window.innerWidth - menuRect.width - offset);
|
|
229
|
+
}
|
|
230
|
+
if (left < offset) {
|
|
231
|
+
left = offset;
|
|
232
|
+
}
|
|
233
|
+
this.menuPosition = { top, left };
|
|
234
|
+
}, 0);
|
|
235
|
+
}
|
|
177
236
|
getFocusableItems() {
|
|
178
237
|
return (this.menuItems?.toArray() ?? [])
|
|
179
238
|
.map((item) => item.nativeElement)
|
|
180
239
|
.filter((item) => !item.hasAttribute('disabled') && !item.hasAttribute('data-disabled'));
|
|
181
240
|
}
|
|
182
241
|
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 });
|
|
242
|
+
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
243
|
}
|
|
185
244
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsActionMenuComponent, decorators: [{
|
|
186
245
|
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" }]
|
|
246
|
+
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
247
|
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { items: [{
|
|
189
248
|
type: Input
|
|
190
249
|
}], align: [{
|
|
@@ -195,17 +254,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
195
254
|
type: Input
|
|
196
255
|
}], disabled: [{
|
|
197
256
|
type: Input
|
|
257
|
+
}], floating: [{
|
|
258
|
+
type: Input
|
|
198
259
|
}], itemSelected: [{
|
|
199
260
|
type: Output
|
|
200
261
|
}], trigger: [{
|
|
201
262
|
type: ViewChild,
|
|
202
263
|
args: ['trigger', { read: ElementRef }]
|
|
264
|
+
}], menu: [{
|
|
265
|
+
type: ViewChild,
|
|
266
|
+
args: ['menu', { read: ElementRef }]
|
|
203
267
|
}], menuItems: [{
|
|
204
268
|
type: ViewChildren,
|
|
205
269
|
args: ['menuItem', { read: ElementRef }]
|
|
206
270
|
}], onDocumentClick: [{
|
|
207
271
|
type: HostListener,
|
|
208
272
|
args: ['document:click', ['$event']]
|
|
273
|
+
}], onWindowResize: [{
|
|
274
|
+
type: HostListener,
|
|
275
|
+
args: ['window:resize']
|
|
209
276
|
}] } });
|
|
210
277
|
|
|
211
278
|
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 ' +
|