UNPKG

@vime/core

Version:

Customizable, extensible, accessible and framework agnostic media player.

432 lines (425 loc) 17.9 kB
import { r as registerInstance, c as createEvent, w as writeTask, h, g as getElement } from './index-f5fd0f81.js'; import { e as isUndefined, c as withComponentRegistry, n as isNil } from './withComponentRegistry-28311671.js'; import { w as withPlayerContext } from './withPlayerContext-4c52f564.js'; import './PlayerEvents-5c5704d6.js'; function unwrapSubmenu(el) { if (el.tagName.toLowerCase() !== 'vm-submenu') return el; const submenu = el; return submenu.shadowRoot.querySelector('vm-menu-item'); } function unwrapRadioGroup(el) { var _a; if (el.tagName.toLowerCase() !== 'vm-menu-radio-group') return el; const radioGroup = el; const slot = radioGroup.shadowRoot.querySelector('slot'); const assignedElements = Array.from((_a = slot === null || slot === void 0 ? void 0 : slot.assignedElements()) !== null && _a !== void 0 ? _a : []); return assignedElements .filter(radio => radio.tagName.toLowerCase() === 'vm-menu-radio') .map(radio => radio.shadowRoot.querySelector('vm-menu-item')); } function menuItemHunter(assignedElements) { if (isUndefined(assignedElements)) return []; const allowed = ['vm-menu-item', 'vm-menu-radio-group', 'vm-submenu']; return Array.from(assignedElements !== null && assignedElements !== void 0 ? assignedElements : []) .filter(el => allowed.includes(el.tagName.toLowerCase())) .map(el => unwrapSubmenu(el)) .map(el => unwrapRadioGroup(el)) .reduce((acc, val) => acc.concat(val), []); } const menuCss = ":host{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;pointer-events:none;z-index:var(--vm-menu-z-index)}:host([active]){pointer-events:auto;z-index:calc(var(--vm-menu-z-index) + 1)}.menu{position:absolute;top:0;left:0;width:100%;height:100%;box-sizing:border-box;transition:var(--vm-menu-transition)}.menu.slideIn{transform:translateX(0)}.menu[aria-hidden='true'].slideInFromLeft{transform:translateX(-100%)}.menu[aria-hidden='true'].slideInFromRight{transform:translateX(100%)}.container{display:flex;flex-direction:column;position:relative;text-align:left;width:100%;height:100%;color:var(--vm-menu-color);background:var(--vm-menu-bg);font-size:var(--vm-menu-font-size);font-weight:var(--vm-menu-font-weight)}.menu:focus{outline:0}"; var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; const Menu = class { constructor(hostRef) { registerInstance(this, hostRef); this.vmOpen = createEvent(this, "vmOpen", 7); this.vmClose = createEvent(this, "vmClose", 7); this.vmFocus = createEvent(this, "vmFocus", 7); this.vmBlur = createEvent(this, "vmBlur", 7); this.vmActiveSubmenuChange = createEvent(this, "vmActiveSubmenuChange", 7); this.vmActiveMenuItemChange = createEvent(this, "vmActiveMenuItemChange", 7); this.vmMenuHeightChange = createEvent(this, "vmMenuHeightChange", 3); this.hasDisconnected = false; /** * Whether the menu is open/visible. */ this.active = false; withComponentRegistry(this); } onActiveMenuitemChange() { this.vmActiveMenuItemChange.emit(this.activeMenuItem); } onActiveSubmenuChange() { this.vmActiveSubmenuChange.emit(this.activeSubmenu); } onActiveChange() { var _a; if (this.hasDisconnected) return; this.active ? this.vmOpen.emit(this.host) : this.vmClose.emit(this.host); if (((_a = this.controller) === null || _a === void 0 ? void 0 : _a.tagName.toLowerCase()) === 'vm-menu-item') { this.controller.expanded = true; } } connectedCallback() { this.hasDisconnected = false; } componentDidRender() { writeTask(() => { if (!this.hasDisconnected) this.calculateHeight(); }); } disconnectedCallback() { this.controller = undefined; this.hasDisconnected = true; } /** * Focuses the menu. */ focusMenu() { var _a; return __awaiter$1(this, void 0, void 0, function* () { (_a = this.menu) === null || _a === void 0 ? void 0 : _a.focus(); }); } /** * Removes focus from the menu. */ blurMenu() { var _a; return __awaiter$1(this, void 0, void 0, function* () { (_a = this.menu) === null || _a === void 0 ? void 0 : _a.blur(); }); } /** * Returns the currently focused menu item. */ getActiveMenuItem() { return __awaiter$1(this, void 0, void 0, function* () { return this.activeMenuItem; }); } /** * Sets the currently focused menu item. */ setActiveMenuItem(item) { return __awaiter$1(this, void 0, void 0, function* () { item === null || item === void 0 ? void 0 : item.focusItem(); this.activeMenuItem = item; }); } /** * Calculates the height of the settings menu based on its children. */ calculateHeight() { var _a, _b; return __awaiter$1(this, void 0, void 0, function* () { let height = 0; if (this.activeSubmenu) { const submenu = yield this.activeSubmenu.getMenu(); height = (_a = (yield (submenu === null || submenu === void 0 ? void 0 : submenu.calculateHeight()))) !== null && _a !== void 0 ? _a : 0; height += yield this.activeSubmenu.getControllerHeight(); } else { const children = ((_b = this.container) === null || _b === void 0 ? void 0 : _b.firstChild).assignedElements({ flatten: true }); children === null || children === void 0 ? void 0 : children.forEach(child => { height += parseFloat(window.getComputedStyle(child).height); }); } this.vmMenuHeightChange.emit(height); return height; }); } onOpenSubmenu(event) { event.stopPropagation(); if (!isUndefined(this.activeSubmenu)) this.activeSubmenu.active = false; this.activeSubmenu = event.detail; this.getChildren().forEach(child => { if (child !== this.activeSubmenu) { child.style.opacity = '0'; child.style.visibility = 'hidden'; } }); writeTask(() => { this.activeSubmenu.active = true; }); } onCloseSubmenu(event) { event === null || event === void 0 ? void 0 : event.stopPropagation(); if (!isUndefined(this.activeSubmenu)) this.activeSubmenu.active = false; this.getChildren().forEach(child => { if (child !== this.activeSubmenu) { child.style.opacity = ''; child.style.visibility = ''; } }); writeTask(() => { this.activeSubmenu = undefined; }); } onWindowClick() { this.onCloseSubmenu(); this.onClose(); } onWindowKeyDown(event) { if (this.active && event.key === 'Escape') { this.onCloseSubmenu(); this.onClose(); this.focusController(); } } getChildren() { var _a; const assignedElements = (_a = this.host .shadowRoot.querySelector('slot')) === null || _a === void 0 ? void 0 : _a.assignedElements({ flatten: true }); return (assignedElements !== null && assignedElements !== void 0 ? assignedElements : []); } getMenuItems() { var _a; const assignedElements = (_a = this.host .shadowRoot.querySelector('slot')) === null || _a === void 0 ? void 0 : _a.assignedElements({ flatten: true }); return menuItemHunter(assignedElements); } focusController() { var _a, _b, _c, _d, _e; if (!isUndefined((_a = this.controller) === null || _a === void 0 ? void 0 : _a.focusItem)) { (_b = this.controller) === null || _b === void 0 ? void 0 : _b.focusItem(); } else if (!isUndefined((_c = this.controller) === null || _c === void 0 ? void 0 : _c.focusControl)) { (_d = this.controller) === null || _d === void 0 ? void 0 : _d.focusControl(); } else { (_e = this.controller) === null || _e === void 0 ? void 0 : _e.focus(); } } triggerMenuItem() { var _a; if (isUndefined(this.activeMenuItem)) return; this.activeMenuItem.click(); // If it controls a menu then focus it essentially opening it. (_a = this.activeMenuItem.menu) === null || _a === void 0 ? void 0 : _a.focusMenu(); } onClose() { this.activeMenuItem = undefined; this.active = false; } onClick(event) { // Stop the event from propagating while playing with menu so that when it is clicked outside // the menu we can close it in the `onWindowClick` handler above. event.stopPropagation(); } onFocus() { var _a; this.active = true; [this.activeMenuItem] = this.getMenuItems(); (_a = this.activeMenuItem) === null || _a === void 0 ? void 0 : _a.focusItem(); this.vmFocus.emit(); } onBlur() { this.vmBlur.emit(); } foucsMenuItem(items, index) { if (index < 0) index = items.length - 1; if (index > items.length - 1) index = 0; this.activeMenuItem = items[index]; this.activeMenuItem.focusItem(); } onKeyDown(event) { if (!this.active) return; event.preventDefault(); event.stopPropagation(); const items = this.getMenuItems(); let index = items.findIndex(item => item === this.activeMenuItem); switch (event.key) { case 'Escape': this.onClose(); this.focusController(); break; case 'ArrowDown': case 'Tab': this.foucsMenuItem(items, (index += 1)); break; case 'ArrowUp': this.foucsMenuItem(items, (index -= 1)); break; case 'ArrowLeft': this.onClose(); this.focusController(); break; case 'ArrowRight': case 'Enter': case ' ': this.triggerMenuItem(); break; case 'Home': case 'PageUp': this.foucsMenuItem(items, 0); break; case 'End': case 'PageDown': this.foucsMenuItem(items, items.length - 1); break; } } render() { var _a, _b, _c; return (h("div", { id: this.identifier, class: { menu: true, slideIn: !isUndefined(this.slideInDirection), slideInFromLeft: this.slideInDirection === 'left', slideInFromRight: this.slideInDirection === 'right', }, role: "menu", tabindex: "-1", "aria-labelledby": (_b = (_a = this.controller) === null || _a === void 0 ? void 0 : _a.identifier) !== null && _b !== void 0 ? _b : (_c = this.controller) === null || _c === void 0 ? void 0 : _c.id, "aria-hidden": !this.active ? 'true' : 'false', onFocus: this.onFocus.bind(this), onBlur: this.onBlur.bind(this), onClick: this.onClick.bind(this), onKeyDown: this.onKeyDown.bind(this), ref: el => { this.menu = el; } }, h("div", { class: "container", ref: el => { this.container = el; } }, h("slot", null)))); } get host() { return getElement(this); } static get watchers() { return { "activeMenuItem": ["onActiveMenuitemChange"], "activeSubmenu": ["onActiveSubmenuChange"], "active": ["onActiveChange"] }; } }; Menu.style = menuCss; const menuItemCss = ":host{display:block}.menuItem{display:flex;position:relative;align-items:center;flex-direction:row;cursor:pointer;color:var(--vm-menu-color);background:var(--vm-menu-bg);font-size:var(--vm-menu-font-size);font-weight:var(--vm-menu-font-weight);padding:var(--vm-menu-item-padding);touch-action:manipulation;box-sizing:border-box}.menuItem:focus{outline:0}.menuItem.hidden{display:none}.menuItem.tapHighlight{background:var(--vm-menu-item-tap-highlight)}.menuItem.showDivider{border-bottom:0.5px solid var(--vm-menu-item-divider-color)}.menuItem.notTouch:hover,.menuItem.notTouch:focus{outline:0;color:var(--vm-menu-item-focus-color);background-color:var(--vm-menu-item-focus-bg)}.menuItem[aria-expanded='true']{position:absolute;z-index:2;top:0;width:100%}.menuItem[aria-hidden='true']{display:none}.menuItem[aria-checked='true'] vm-icon{opacity:1;visibility:visible}vm-icon{display:inline-block}vm-icon{fill:currentColor;pointer-events:none;font-size:var(--vm-menu-item-check-icon-size);margin-right:10px;opacity:0;visibility:hidden;transition:var(--vm-fade-transition)}.hint{display:inline-block;margin-left:auto;overflow:hidden;pointer-events:none;margin-right:6px;font-size:var(--vm-menu-item-hint-font-size);opacity:var(--vm-menu-item-hint-opacity);color:var(--vm-menu-item-hint-color)}.badge{display:inline-block;line-height:1;overflow:hidden;pointer-events:none;margin-left:6px;color:var(--vm-menu-item-badge-color);background:var(--vm-menu-item-badge-bg);font-size:var(--vm-menu-item-badge-font-size)}.spacer{flex:1}.arrow{color:var(--vm-menu-item-arrow-color);border:2px solid;padding:2px;display:inline-block;border-width:0 2px 2px 0}.arrow.left{margin-right:6px;transform:rotate(135deg)}.arrow.right{transform:rotate(-45deg);opacity:0.38}"; var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; const MenuItem = class { constructor(hostRef) { registerInstance(this, hostRef); this.vmFocus = createEvent(this, "vmFocus", 7); this.vmBlur = createEvent(this, "vmBlur", 7); this.showTapHighlight = false; /** * Whether the item is displayed or not. */ this.hidden = false; /** * The name of the checkmark icon to resolve from the icon library. */ this.checkIcon = 'check'; /** @internal */ this.isTouch = false; withComponentRegistry(this); withPlayerContext(this, ['isTouch']); } /** * Focuses the menu item. */ focusItem() { var _a; return __awaiter(this, void 0, void 0, function* () { (_a = this.menuItem) === null || _a === void 0 ? void 0 : _a.focus(); }); } /** * Removes focus from the menu item. */ blurItem() { var _a; return __awaiter(this, void 0, void 0, function* () { (_a = this.menuItem) === null || _a === void 0 ? void 0 : _a.blur(); }); } /** * Returns the height of the menu item. */ getHeight() { return __awaiter(this, void 0, void 0, function* () { return parseFloat(this.menuItem ? window.getComputedStyle(this.menuItem).height : '0'); }); } onClick() { if (!isNil(this.menu)) this.menu.active = !this.expanded; } onFocus() { this.vmFocus.emit(); } onBlur() { this.vmBlur.emit(); } onTouchStart() { this.showTapHighlight = true; } onTouchEnd() { setTimeout(() => { this.showTapHighlight = false; }, 100); } onMouseLeave() { var _a; (_a = this.menuItem) === null || _a === void 0 ? void 0 : _a.blur(); } render() { var _a, _b, _c, _d; const isCheckedDefined = !isUndefined(this.checked); const isMenuDefined = !isUndefined(this.menu); const hasExpanded = this.expanded ? 'true' : 'false'; const isChecked = this.checked ? 'true' : 'false'; const showCheckedIcon = isCheckedDefined && !isUndefined(this.checkIcon); const showLeftNavArrow = isMenuDefined && this.expanded; const showRightNavArrow = isMenuDefined && !this.expanded; const showHint = !isUndefined(this.hint) && !isCheckedDefined && (!isMenuDefined || !this.expanded); const showBadge = !isUndefined(this.badge) && !showHint && !showRightNavArrow; const hasSpacer = showHint || showRightNavArrow; return (h("div", { class: { menuItem: true, notTouch: !this.isTouch, tapHighlight: this.showTapHighlight, showDivider: isMenuDefined && ((_a = this.expanded) !== null && _a !== void 0 ? _a : false), }, id: this.identifier, role: isCheckedDefined ? 'menuitemradio' : 'menuitem', tabindex: "0", "aria-label": this.label, "aria-hidden": this.hidden ? 'true' : 'false', "aria-haspopup": isMenuDefined ? 'true' : undefined, "aria-controls": (_c = (_b = this.menu) === null || _b === void 0 ? void 0 : _b.identifier) !== null && _c !== void 0 ? _c : (_d = this.menu) === null || _d === void 0 ? void 0 : _d.id, "aria-expanded": isMenuDefined ? hasExpanded : undefined, "aria-checked": isCheckedDefined ? isChecked : undefined, onClick: this.onClick.bind(this), onFocus: this.onFocus.bind(this), onBlur: this.onBlur.bind(this), onTouchStart: this.onTouchStart.bind(this), onTouchEnd: this.onTouchEnd.bind(this), onMouseLeave: this.onMouseLeave.bind(this), ref: el => { this.menuItem = el; } }, showCheckedIcon && (h("vm-icon", { name: this.checkIcon, library: this.icons })), showLeftNavArrow && h("span", { class: "arrow left" }), this.label, hasSpacer && h("span", { class: "spacer" }), showHint && h("span", { class: "hint" }, this.hint), showBadge && h("span", { class: "badge" }, this.badge), showRightNavArrow && h("span", { class: "arrow right" }))); } get host() { return getElement(this); } }; MenuItem.style = menuItemCss; export { Menu as vm_menu, MenuItem as vm_menu_item };