- added NcModal.vue, NcActions.vue, NcButton.vue, FirstrunWizard.vue, Card.vue, Page0.vue, Page1.vue, Page2.vue, Page3.vue and some icons
Some checks failed
CI Pipeline / japa-tests (push) Failing after 51s
Some checks failed
CI Pipeline / japa-tests (push) Failing after 51s
- added lime color inside tailwind.config.js - added some utilities scripts needed for components - npm updates - changed postcss.config.js for nesting css styles - added about function to NavBar.vue
This commit is contained in:
parent
cefd9081ae
commit
87e9314b00
25 changed files with 3475 additions and 401 deletions
774
resources/js/Components/NcActions.vue
Normal file
774
resources/js/Components/NcActions.vue
Normal file
|
@ -0,0 +1,774 @@
|
|||
<script lang="ts">
|
||||
// import NcButton from '../NcButton/index.js'
|
||||
import BaseButton from '@/Components/BaseButton.vue';
|
||||
// import NcPopover from '../NcPopover/index.js'
|
||||
import GenRandomId from '../utils/GenRandomId';
|
||||
// import { t } from '../../l10n.js'
|
||||
|
||||
import { computed } from 'vue';
|
||||
import DotsHorizontal from '@/Components/Icons/DotsHorizontal.vue';
|
||||
|
||||
const focusableSelector = '.focusable';
|
||||
|
||||
/**
|
||||
* The Actions component can be used to display one ore more actions.
|
||||
* If only a single action is provided, it will be rendered as an inline icon.
|
||||
* For more, a menu indicator will be shown and a popovermenu containing the
|
||||
* actions will be opened on click.
|
||||
*
|
||||
* @since 0.10.0
|
||||
*/
|
||||
export default {
|
||||
name: 'NcActions',
|
||||
|
||||
components: {
|
||||
// NcButton,
|
||||
BaseButton,
|
||||
DotsHorizontal,
|
||||
// NcPopover,
|
||||
},
|
||||
|
||||
provide() {
|
||||
return {
|
||||
/**
|
||||
* NcActions can be used as:
|
||||
* - Application menu (has menu role)
|
||||
* - Navigation (has no specific role, should be used an element with navigation role)
|
||||
* - Popover with plain text or text inputs (has no specific role)
|
||||
* Depending on the usage (used items), the menu and its items should have different roles for a11y.
|
||||
* Provide the role for NcAction* components in the NcActions content.
|
||||
* @type {import('vue').ComputedRef<boolean>}
|
||||
*/
|
||||
'NcActions:isSemanticMenu': computed(() => this.isSemanticMenu),
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
/**
|
||||
* Specify the open state of the popover menu
|
||||
*/
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* This disables the internal open management,
|
||||
* so the actions menu only respects the `open` prop.
|
||||
* This is e.g. necessary for the NcAvatar component
|
||||
* to only open the actions menu after loading it's entries has finished.
|
||||
*/
|
||||
manualOpen: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Force the actions to display in a three dot menu
|
||||
*/
|
||||
forceMenu: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Force the name to show for single actions
|
||||
*/
|
||||
forceName: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Specify the menu name
|
||||
*/
|
||||
menuName: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply primary styling for this menu
|
||||
*/
|
||||
primary: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies the button type used for trigger and single actions buttons
|
||||
* Accepted values: primary, secondary, tertiary, tertiary-no-background, tertiary-on-primary, error, warning, success. If left empty,
|
||||
* the default button style will be applied.
|
||||
*/
|
||||
type: {
|
||||
type: String,
|
||||
validator(value: string) {
|
||||
return (
|
||||
[
|
||||
'primary',
|
||||
'secondary',
|
||||
'tertiary',
|
||||
'tertiary-no-background',
|
||||
'tertiary-on-primary',
|
||||
'error',
|
||||
'warning',
|
||||
'success',
|
||||
].indexOf(value) !== -1
|
||||
);
|
||||
},
|
||||
default: null,
|
||||
},
|
||||
|
||||
/**
|
||||
* Icon to show for the toggle menu button
|
||||
* when more than one action is inside the actions component.
|
||||
* Only replace the default three-dot icon if really necessary.
|
||||
*/
|
||||
defaultIcon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
/**
|
||||
* Aria label for the actions menu.
|
||||
*
|
||||
* If `menuName` is defined this will not be used to prevent
|
||||
* any accessible name conflicts. This ensures that the
|
||||
* element can be activated via voice input.
|
||||
*/
|
||||
ariaLabel: {
|
||||
type: String,
|
||||
default: 'Actions',
|
||||
},
|
||||
|
||||
/**
|
||||
* @deprecated To be removed in @nextcloud/vue 9. Migration guide: remove ariaHidden prop from NcAction* components.
|
||||
* @todo Add a check in @nextcloud/vue 9 that this prop is not provided,
|
||||
* otherwise root element will inherit incorrect aria-hidden.
|
||||
*/
|
||||
ariaHidden: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
|
||||
/**
|
||||
* Wanted direction of the menu
|
||||
*/
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
},
|
||||
|
||||
/**
|
||||
* DOM element for the actions' popover boundaries
|
||||
*/
|
||||
boundariesElement: {
|
||||
type: Element,
|
||||
default: () => document.querySelector('body'),
|
||||
},
|
||||
|
||||
/**
|
||||
* Selector for the actions' popover container
|
||||
*/
|
||||
container: {
|
||||
type: [String, Object, Element, Boolean],
|
||||
default: 'body',
|
||||
},
|
||||
|
||||
/**
|
||||
* Disabled state of the main button (single action or menu toggle)
|
||||
*/
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Display x items inline out of the dropdown menu
|
||||
* Will be ignored if `forceMenu` is set
|
||||
*/
|
||||
inline: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['open', 'update:open', 'close', 'focus', 'blur'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
opened: this.open,
|
||||
focusIndex: 0,
|
||||
randomId: `menu-${GenRandomId()}`,
|
||||
isSemanticMenu: false,
|
||||
isSemanticNavigation: false,
|
||||
isSemanticPopoverLike: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
triggerBtnType() {
|
||||
// If requested, we use a primary button
|
||||
return (
|
||||
this.type ||
|
||||
(this.primary
|
||||
? 'primary'
|
||||
: // If it has a name, we use a secondary button
|
||||
this.menuName
|
||||
? 'secondary'
|
||||
: 'tertiary')
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
// Watch parent prop
|
||||
open(state) {
|
||||
if (state === this.opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.opened = state;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Do we have exactly one Action and
|
||||
* is it allowed as a standalone element?
|
||||
*
|
||||
* @param {Array} action The action to check
|
||||
* @return {boolean}
|
||||
*/
|
||||
isValidSingleAction(action) {
|
||||
const componentName = action?.componentOptions?.Ctor?.extendOptions?.name ?? action?.componentOptions?.tag;
|
||||
return ['NcActionButton', 'NcActionLink', 'NcActionRouter'].includes(componentName);
|
||||
},
|
||||
|
||||
// MENU STATE MANAGEMENT
|
||||
openMenu() {
|
||||
if (this.opened) {
|
||||
return;
|
||||
}
|
||||
this.opened = true;
|
||||
|
||||
/**
|
||||
* Event emitted when the popover menu open state is changed
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.$emit('update:open', true);
|
||||
|
||||
/**
|
||||
* Event emitted when the popover menu is opened
|
||||
*/
|
||||
this.$emit('open');
|
||||
},
|
||||
closeMenu(returnFocus = true) {
|
||||
if (!this.opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.opened = false;
|
||||
|
||||
this.$refs.popover.clearFocusTrap({ returnFocus });
|
||||
|
||||
/**
|
||||
* Event emitted when the popover menu open state is changed
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.$emit('update:open', false);
|
||||
|
||||
/**
|
||||
* Event emitted when the popover menu is closed
|
||||
*/
|
||||
this.$emit('close');
|
||||
|
||||
// close everything
|
||||
this.focusIndex = 0;
|
||||
|
||||
// focus back the menu button
|
||||
this.$refs.menuButton.$el.focus();
|
||||
},
|
||||
|
||||
onOpen(event) {
|
||||
this.$nextTick(() => {
|
||||
this.focusFirstAction(event);
|
||||
});
|
||||
},
|
||||
|
||||
// MENU KEYS & FOCUS MANAGEMENT
|
||||
// focus nearest focusable item on mouse move
|
||||
// DO NOT change the focus if the target is already focused
|
||||
// this will prevent issues with input being unfocused
|
||||
// on mouse move
|
||||
onMouseFocusAction(event) {
|
||||
if (document.activeElement === event.target) {
|
||||
return;
|
||||
}
|
||||
|
||||
const menuItem = event.target.closest('li');
|
||||
if (menuItem && this.$refs.menu.contains(menuItem)) {
|
||||
const focusableItem = menuItem.querySelector(focusableSelector);
|
||||
if (focusableItem) {
|
||||
const focusList = this.$refs.menu.querySelectorAll(focusableSelector);
|
||||
const focusIndex = [...focusList].indexOf(focusableItem);
|
||||
if (focusIndex > -1) {
|
||||
this.focusIndex = focusIndex;
|
||||
this.focusAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Dispatches the keydown listener to different handlers
|
||||
*
|
||||
* @param {object} event The keydown event
|
||||
*/
|
||||
onKeydown(event) {
|
||||
if (event.key === 'Tab' && !this.isSemanticPopoverLike) {
|
||||
this.closeMenu(false);
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowUp') {
|
||||
this.focusPreviousAction(event);
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
this.focusNextAction(event);
|
||||
}
|
||||
|
||||
if (event.key === 'PageUp') {
|
||||
this.focusFirstAction(event);
|
||||
}
|
||||
|
||||
if (event.key === 'PageDown') {
|
||||
this.focusLastAction(event);
|
||||
}
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
this.closeMenu();
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
removeCurrentActive() {
|
||||
const currentActiveElement = this.$refs.menu.querySelector('li.active');
|
||||
if (currentActiveElement) {
|
||||
currentActiveElement.classList.remove('active');
|
||||
}
|
||||
},
|
||||
focusAction() {
|
||||
// TODO: have a global disabled state for non input elements
|
||||
const focusElement = this.$refs.menu.querySelectorAll(focusableSelector)[this.focusIndex];
|
||||
if (focusElement) {
|
||||
this.removeCurrentActive();
|
||||
const liMenuParent = focusElement.closest('li.action');
|
||||
focusElement.focus();
|
||||
if (liMenuParent) {
|
||||
liMenuParent.classList.add('active');
|
||||
}
|
||||
}
|
||||
},
|
||||
focusPreviousAction(event) {
|
||||
if (this.opened) {
|
||||
if (this.focusIndex === 0) {
|
||||
this.focusLastAction(event);
|
||||
} else {
|
||||
this.preventIfEvent(event);
|
||||
this.focusIndex = this.focusIndex - 1;
|
||||
}
|
||||
this.focusAction();
|
||||
}
|
||||
},
|
||||
focusNextAction(event) {
|
||||
if (this.opened) {
|
||||
const indexLength = this.$refs.menu.querySelectorAll(focusableSelector).length - 1;
|
||||
if (this.focusIndex === indexLength) {
|
||||
this.focusFirstAction(event);
|
||||
} else {
|
||||
this.preventIfEvent(event);
|
||||
this.focusIndex = this.focusIndex + 1;
|
||||
}
|
||||
this.focusAction();
|
||||
}
|
||||
},
|
||||
focusFirstAction(event) {
|
||||
if (this.opened) {
|
||||
this.preventIfEvent(event);
|
||||
// In case a button is considered aria-selected we will use this one as a initial focus
|
||||
const firstSelectedIndex = [...this.$refs.menu.querySelectorAll(focusableSelector)].findIndex((button) => {
|
||||
return button.parentElement.getAttribute('aria-selected');
|
||||
});
|
||||
this.focusIndex = firstSelectedIndex > -1 ? firstSelectedIndex : 0;
|
||||
this.focusAction();
|
||||
}
|
||||
},
|
||||
focusLastAction(event) {
|
||||
if (this.opened) {
|
||||
this.preventIfEvent(event);
|
||||
this.focusIndex = this.$refs.menu.querySelectorAll(focusableSelector).length - 1;
|
||||
this.focusAction();
|
||||
}
|
||||
},
|
||||
preventIfEvent(event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
onFocus(event) {
|
||||
this.$emit('focus', event);
|
||||
},
|
||||
onBlur(event) {
|
||||
this.$emit('blur', event);
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* The render function to display the component
|
||||
*
|
||||
* @param {Function} h The function to create VNodes
|
||||
* @return {object|undefined} The created VNode
|
||||
*/
|
||||
render(h) {
|
||||
/**
|
||||
* Filter the Actions, so that we only get allowed components.
|
||||
* This also ensure that we don't get 'text' elements, which would
|
||||
* become problematic later on.
|
||||
*/
|
||||
const actions = ([this.$slots.default] || []).filter(
|
||||
(action) => action?.componentOptions?.tag || action?.componentOptions?.Ctor?.extendOptions?.name,
|
||||
);
|
||||
|
||||
const getActionName = (action) => action?.componentOptions?.Ctor?.extendOptions?.name ?? action?.componentOptions?.tag;
|
||||
|
||||
const menuItemsActions = ['NcActionButton', 'NcActionButtonGroup', 'NcActionCheckbox', 'NcActionRadio'];
|
||||
const textInputActions = ['NcActionInput', 'NcActionTextEditable'];
|
||||
const linkActions = ['NcActionLink', 'NcActionRouter'];
|
||||
|
||||
const hasTextInputAction = actions.some((action) => textInputActions.includes(getActionName(action)));
|
||||
const hasMenuItemAction = actions.some((action) => menuItemsActions.includes(getActionName(action)));
|
||||
const hasLinkAction = actions.some((action) => linkActions.includes(getActionName(action)));
|
||||
|
||||
// We consider the NcActions to have role="menu" if it consists some button-like action and not text inputs
|
||||
this.isSemanticMenu = hasMenuItemAction && !hasTextInputAction;
|
||||
// We consider the NcActions to be navigation if it consists some link-like action
|
||||
this.isSemanticNavigation = hasLinkAction && !hasMenuItemAction && !hasTextInputAction;
|
||||
// If it is no a manu and not a navigation, it is a popover with items: a form or just a text
|
||||
this.isSemanticPopoverLike = !this.isSemanticMenu && !this.isSemanticNavigation;
|
||||
|
||||
/**
|
||||
* Filter and list actions that are allowed to be displayed inline
|
||||
*/
|
||||
let inlineActions = actions.filter(this.isValidSingleAction);
|
||||
if (this.forceMenu && inlineActions.length > 0 && this.inline > 0) {
|
||||
// Vue.util.warn('Specifying forceMenu will ignore any inline actions rendering.');
|
||||
inlineActions = [];
|
||||
}
|
||||
|
||||
// Check that we have at least one action
|
||||
if (actions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the provided action
|
||||
*
|
||||
* @param {import('vue').VNode} action the action to render
|
||||
* @return {Function} the vue render function
|
||||
*/
|
||||
const renderInlineAction = (action) => {
|
||||
const icon =
|
||||
action?.data?.scopedSlots?.icon()?.[0] || h('span', { class: ['icon', action?.componentOptions?.propsData?.icon] });
|
||||
const attrs = action?.data?.attrs || {};
|
||||
const clickListener = action?.componentOptions?.listeners?.click;
|
||||
|
||||
const text = action?.componentOptions?.children?.[0]?.text?.trim?.();
|
||||
const ariaLabel = action?.componentOptions?.propsData?.ariaLabel || text;
|
||||
const buttonText = this.forceName ? text : '';
|
||||
|
||||
let title = action?.componentOptions?.propsData?.title;
|
||||
// Show a default title for single actions if none is present
|
||||
if (!(this.forceName || title)) {
|
||||
title = text;
|
||||
}
|
||||
|
||||
return h(
|
||||
'BaseButton',
|
||||
{
|
||||
class: ['action-item action-item--single', action?.data?.staticClass, action?.data?.class],
|
||||
attrs: {
|
||||
...attrs,
|
||||
'aria-label': ariaLabel,
|
||||
title,
|
||||
},
|
||||
ref: action?.data?.ref,
|
||||
props: {
|
||||
// If it has a menuName, we use a secondary button
|
||||
type: this.type || (buttonText ? 'secondary' : 'tertiary'),
|
||||
disabled: this.disabled || action?.componentOptions?.propsData?.disabled,
|
||||
...action?.componentOptions?.propsData,
|
||||
},
|
||||
on: {
|
||||
focus: this.onFocus,
|
||||
blur: this.onBlur,
|
||||
// If we have a click listener,
|
||||
// we bind it to execute on click and forward the click event
|
||||
...(!!clickListener && {
|
||||
click: (event) => {
|
||||
if (clickListener) {
|
||||
clickListener(event);
|
||||
}
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
[h('template', { slot: 'icon' }, [icon]), buttonText],
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the actions popover
|
||||
*
|
||||
* @param {Array} actions the actions to render within
|
||||
* @return {Function} the vue render function
|
||||
*/
|
||||
const renderActionsPopover = (actions) => {
|
||||
const triggerIcon =
|
||||
this.$slots.icon?.[0] ||
|
||||
(this.defaultIcon
|
||||
? h('span', { class: ['icon', this.defaultIcon] })
|
||||
: h('DotsHorizontal', {
|
||||
props: {
|
||||
size: 20,
|
||||
},
|
||||
}));
|
||||
return h(
|
||||
'NcPopover',
|
||||
{
|
||||
ref: 'popover',
|
||||
props: {
|
||||
delay: 0,
|
||||
handleResize: true,
|
||||
shown: this.opened,
|
||||
placement: this.placement,
|
||||
boundary: this.boundariesElement,
|
||||
container: this.container,
|
||||
popoverBaseClass: 'action-item__popper',
|
||||
// Menu and navigation should not have focus trap
|
||||
// Tab should close the menu and move focus to the next UI element
|
||||
setReturnFocus: !this.isSemanticPopoverLike ? null : this.$refs.menuButton?.$el,
|
||||
focusTrap: this.isSemanticPopoverLike,
|
||||
},
|
||||
// For some reason the popover component
|
||||
// does not react to props given under the 'props' key,
|
||||
// so we use both 'attrs' and 'props'
|
||||
attrs: {
|
||||
delay: 0,
|
||||
handleResize: true,
|
||||
shown: this.opened,
|
||||
placement: this.placement,
|
||||
boundary: this.boundariesElement,
|
||||
container: this.container,
|
||||
...(this.manualOpen && { triggers: [] }),
|
||||
},
|
||||
on: {
|
||||
'show': this.openMenu,
|
||||
'after-show': this.onOpen,
|
||||
'hide': this.closeMenu,
|
||||
},
|
||||
},
|
||||
[
|
||||
h(
|
||||
'BaseButton',
|
||||
{
|
||||
class: 'action-item__menutoggle',
|
||||
props: {
|
||||
type: this.triggerBtnType,
|
||||
disabled: this.disabled,
|
||||
},
|
||||
slot: 'trigger',
|
||||
ref: 'menuButton',
|
||||
attrs: {
|
||||
'aria-haspopup': this.isSemanticMenu ? 'menu' : null,
|
||||
'aria-label': this.menuName ? null : this.ariaLabel,
|
||||
'aria-controls': this.opened ? this.randomId : null,
|
||||
'aria-expanded': this.opened ? 'true' : 'false',
|
||||
},
|
||||
on: {
|
||||
focus: this.onFocus,
|
||||
blur: this.onBlur,
|
||||
},
|
||||
},
|
||||
[h('template', { slot: 'icon' }, [triggerIcon]), this.menuName],
|
||||
),
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
class: {
|
||||
open: this.opened,
|
||||
},
|
||||
attrs: {
|
||||
tabindex: '-1',
|
||||
},
|
||||
on: {
|
||||
keydown: this.onKeydown,
|
||||
mousemove: this.onMouseFocusAction,
|
||||
},
|
||||
ref: 'menu',
|
||||
},
|
||||
[
|
||||
h(
|
||||
'ul',
|
||||
{
|
||||
attrs: {
|
||||
id: this.randomId,
|
||||
tabindex: '-1',
|
||||
role: this.isSemanticMenu ? 'menu' : undefined,
|
||||
},
|
||||
},
|
||||
[actions],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* If we have a single action only and didn't force a menu,
|
||||
* we render the action as a standalone button
|
||||
*/
|
||||
if (actions.length === 1 && inlineActions.length === 1 && !this.forceMenu) {
|
||||
return renderInlineAction(inlineActions[0]);
|
||||
}
|
||||
|
||||
// If we completely re-render the children
|
||||
// we need to focus the first action again
|
||||
// Mostly used when clicking a menu item
|
||||
this.$nextTick(() => {
|
||||
if (this.opened && this.$refs.menu) {
|
||||
const isAnyActive = this.$refs.menu.querySelector('li.active') || [];
|
||||
if (isAnyActive.length === 0) {
|
||||
this.focusFirstAction();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* If we some inline actions to render, render them, then the menu
|
||||
*/
|
||||
if (inlineActions.length > 0 && this.inline > 0) {
|
||||
const renderedInlineActions = inlineActions.slice(0, this.inline);
|
||||
// Filter already rendered actions
|
||||
const menuActions = actions.filter((action) => !renderedInlineActions.includes(action));
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
class: ['action-items', `action-item--${this.triggerBtnType}`],
|
||||
},
|
||||
[
|
||||
// Render inline actions
|
||||
...renderedInlineActions.map(renderInlineAction),
|
||||
// render the rest within the popover menu
|
||||
menuActions.length > 0
|
||||
? h(
|
||||
'div',
|
||||
{
|
||||
class: [
|
||||
'action-item',
|
||||
{
|
||||
'action-item--open': this.opened,
|
||||
},
|
||||
],
|
||||
},
|
||||
[renderActionsPopover(menuActions)],
|
||||
)
|
||||
: null,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Otherwise, we render the actions in a popover
|
||||
*/
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
class: [
|
||||
'action-item action-item--default-popover',
|
||||
`action-item--${this.triggerBtnType}`,
|
||||
{
|
||||
'action-item--open': this.opened,
|
||||
},
|
||||
],
|
||||
},
|
||||
[renderActionsPopover(actions)],
|
||||
);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
/* Inline buttons */
|
||||
.action-items {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
/* Spacing between buttons */
|
||||
/* &>button {
|
||||
margin-right: math.div(28, 2);
|
||||
} */
|
||||
}
|
||||
|
||||
.action-item {
|
||||
/* --open-background-color: var(--color-background-hover, $action-background-hover); */
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
&.action-item--primary {
|
||||
--open-background-color: var(--color-primary-element-hover);
|
||||
}
|
||||
|
||||
&.action-item--secondary {
|
||||
--open-background-color: var(--color-primary-element-light-hover);
|
||||
}
|
||||
|
||||
&.action-item--error {
|
||||
--open-background-color: var(--color-error-hover);
|
||||
}
|
||||
|
||||
&.action-item--warning {
|
||||
--open-background-color: var(--color-warning-hover);
|
||||
}
|
||||
|
||||
&.action-item--success {
|
||||
--open-background-color: var(--color-success-hover);
|
||||
}
|
||||
|
||||
&.action-item--tertiary-no-background {
|
||||
--open-background-color: transparent;
|
||||
}
|
||||
|
||||
&.action-item--open .action-item__menutoggle {
|
||||
background-color: var(--open-background-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="css">
|
||||
/* // We overwrote the popover base class, so we can style
|
||||
// the popover__inner for actions only. */
|
||||
.v-popper--theme-dropdown.v-popper__popper.action-item__popper .v-popper__wrapper {
|
||||
border-radius: var(--border-radius-large);
|
||||
overflow: hidden;
|
||||
|
||||
.v-popper__inner {
|
||||
border-radius: var(--border-radius-large);
|
||||
padding: 4px;
|
||||
max-height: calc(50vh - 16px);
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue