Add content element action menu
This commit is contained in:
parent
bde635b21c
commit
13a5ea9534
|
|
@ -9,8 +9,6 @@
|
|||
import PlusIcon from '@/components/icons/PlusIcon';
|
||||
export default {
|
||||
components: { PlusIcon }
|
||||
|
||||
//
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -19,8 +17,10 @@
|
|||
$color: $color-silver-dark;
|
||||
|
||||
.add-content-link {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
cursor: pointer;
|
||||
|
||||
&__icon {
|
||||
width: 20px;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
<!-- Add content at top of content block -->
|
||||
<add-content-link
|
||||
class="content-block-form__segment"
|
||||
@click="addBlock(-1)"
|
||||
/>
|
||||
<!-- Loop for outer contents layer -->
|
||||
|
|
@ -39,31 +38,45 @@
|
|||
:key="block.id"
|
||||
>
|
||||
<!-- If the block is a content list -->
|
||||
<ol
|
||||
class="content-list__item content-block-form__segment"
|
||||
data-cy="content-list-item"
|
||||
<div
|
||||
class="content-block-form__segment"
|
||||
v-if="block.type === 'content_list_item'"
|
||||
>
|
||||
<li
|
||||
class="content-block-form__segment"
|
||||
v-for="(content, index) in block.contents"
|
||||
:key="content.id"
|
||||
<content-element-actions
|
||||
class="content-block-form__actions"
|
||||
@remove="remove(outer)"
|
||||
@move-up="up(outer)"
|
||||
@move-down="down(outer)"
|
||||
@move-top="top(outer)"
|
||||
@move-bottom="bottom(outer)"
|
||||
/>
|
||||
<ol
|
||||
class="content-list__item"
|
||||
data-cy="content-list-item"
|
||||
>
|
||||
<content-element
|
||||
:element="content"
|
||||
<li
|
||||
class="content-block-form__segment"
|
||||
@update="update(index, $event, outer)"
|
||||
@remove="remove(outer, index)"
|
||||
@up="up(outer, index)"
|
||||
@down="down(outer, index)"
|
||||
/>
|
||||
v-for="(content, index) in block.contents"
|
||||
:key="content.id"
|
||||
>
|
||||
<content-element
|
||||
:element="content"
|
||||
class="content-block-form__segment"
|
||||
@update="update(index, $event, outer)"
|
||||
@remove="remove(outer, index)"
|
||||
@up="up(outer, index)"
|
||||
@down="down(outer, index)"
|
||||
@top="top(outer, index)"
|
||||
@bottom="bottom(outer, index)"
|
||||
/>
|
||||
|
||||
<add-content-link
|
||||
class="content-block-form__add-button"
|
||||
@click="addBlock(outer, index)"
|
||||
/>
|
||||
</li>
|
||||
</ol>
|
||||
<add-content-link
|
||||
class="content-block-form__add-button"
|
||||
@click="addBlock(outer, index)"
|
||||
/>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<!-- If the block is a single element -->
|
||||
<content-element
|
||||
:element="block"
|
||||
|
|
@ -73,11 +86,12 @@
|
|||
@remove="remove(outer)"
|
||||
@up="up(outer)"
|
||||
@down="down(outer)"
|
||||
@top="top(outer)"
|
||||
@bottom="bottom(outer)"
|
||||
/>
|
||||
|
||||
<!-- Add element after the looped item -->
|
||||
<add-content-link
|
||||
class="content-block-form__segment"
|
||||
@click="addBlock(outer)"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -107,23 +121,25 @@
|
|||
import AddContentLink from '@/components/content-block-form/AddContentLink';
|
||||
|
||||
import ContentElement from '@/components/content-block-form/ContentElement';
|
||||
import {removeAtIndex, replaceAtIndex, swapElements} from '@/graphql/immutable-operations';
|
||||
import {moveToIndex, removeAtIndex, replaceAtIndex, swapElements} from '@/graphql/immutable-operations';
|
||||
|
||||
import {CHOOSER, transformInnerContents} from '@/components/content-block-form/helpers';
|
||||
import ContentElementActions from '@/components/content-block-form/ContentElementActions';
|
||||
|
||||
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
contentBlock: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
ContentElementActions,
|
||||
ContentElement,
|
||||
AddContentLink,
|
||||
InputWithLabel,
|
||||
|
|
@ -144,7 +160,7 @@
|
|||
computed: {
|
||||
isValid() {
|
||||
return this.localContentBlock.title > '';
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
update(index, element, parent) {
|
||||
|
|
@ -153,7 +169,7 @@
|
|||
this.localContentBlock.contents = [
|
||||
...this.localContentBlock.contents.slice(0, index),
|
||||
element,
|
||||
...this.localContentBlock.contents.slice(index + 1)
|
||||
...this.localContentBlock.contents.slice(index + 1),
|
||||
];
|
||||
} else {
|
||||
const parentBlock = this.localContentBlock.contents[parent];
|
||||
|
|
@ -166,9 +182,9 @@
|
|||
...parentBlock.contents.slice(0, index),
|
||||
element,
|
||||
...parentBlock.contents.slice(index + 1),
|
||||
]
|
||||
],
|
||||
},
|
||||
...this.localContentBlock.contents.slice(parent + 1)
|
||||
...this.localContentBlock.contents.slice(parent + 1),
|
||||
];
|
||||
}
|
||||
},
|
||||
|
|
@ -185,20 +201,20 @@
|
|||
id: -1,
|
||||
type: CHOOSER,
|
||||
},
|
||||
...block.contents.slice(innerIndex + 1)
|
||||
]
|
||||
...block.contents.slice(innerIndex + 1),
|
||||
],
|
||||
},
|
||||
...this.localContentBlock.contents.slice(afterOuterIndex + 1)
|
||||
...this.localContentBlock.contents.slice(afterOuterIndex + 1),
|
||||
];
|
||||
} else {
|
||||
this.localContentBlock.contents = [
|
||||
...this.localContentBlock.contents.slice(0, afterOuterIndex+1),
|
||||
...this.localContentBlock.contents.slice(0, afterOuterIndex + 1),
|
||||
{
|
||||
id: -1,
|
||||
type: CHOOSER,
|
||||
includeListOption: true
|
||||
includeListOption: true,
|
||||
},
|
||||
...this.localContentBlock.contents.slice(afterOuterIndex+1)
|
||||
...this.localContentBlock.contents.slice(afterOuterIndex + 1),
|
||||
];
|
||||
}
|
||||
},
|
||||
|
|
@ -219,17 +235,45 @@
|
|||
const outerElement = contents[outer];
|
||||
const newOuterElement = {
|
||||
...outerElement,
|
||||
contents: swapElements(outerElement.contents, inner, inner + distance)
|
||||
contents: swapElements(outerElement.contents, inner, inner + distance),
|
||||
};
|
||||
this.localContentBlock.contents = replaceAtIndex(contents, outer, newOuterElement);
|
||||
}
|
||||
},
|
||||
up(outer, inner){
|
||||
top(outer, inner) {
|
||||
if (inner === undefined) {
|
||||
this.localContentBlock.contents = moveToIndex(this.localContentBlock.contents, outer, 0);
|
||||
} else {
|
||||
const {contents} = this.localContentBlock;
|
||||
const outerElement = contents[outer];
|
||||
const newOuterElement = {
|
||||
...outerElement,
|
||||
contents: moveToIndex(outerElement.contents, inner, 0),
|
||||
};
|
||||
this.localContentBlock.contents = replaceAtIndex(contents, outer, newOuterElement);
|
||||
}
|
||||
},
|
||||
up(outer, inner) {
|
||||
this.shift(outer, inner, -1);
|
||||
},
|
||||
down(outer, inner){
|
||||
down(outer, inner) {
|
||||
this.shift(outer, inner, 1);
|
||||
},
|
||||
bottom(outer, inner) {
|
||||
if (inner === undefined) {
|
||||
const maxIndex = this.localContentBlock.contents.length - 1;
|
||||
this.localContentBlock.contents = moveToIndex(this.localContentBlock.contents, outer, maxIndex);
|
||||
} else {
|
||||
const {contents} = this.localContentBlock;
|
||||
const outerElement = contents[outer];
|
||||
const maxIndex = outerElement.contents.length - 1;
|
||||
const newOuterElement = {
|
||||
...outerElement,
|
||||
contents: moveToIndex(outerElement.contents, inner, maxIndex),
|
||||
};
|
||||
this.localContentBlock.contents = replaceAtIndex(contents, outer, newOuterElement);
|
||||
}
|
||||
},
|
||||
executeRemoval(outer, inner) {
|
||||
if (inner === undefined) {
|
||||
// not a list item container, just remove the element from the outer array
|
||||
|
|
@ -245,7 +289,7 @@
|
|||
*/
|
||||
let element = {
|
||||
...this.localContentBlock.contents[outer],
|
||||
contents: innerContents
|
||||
contents: innerContents,
|
||||
};
|
||||
this.localContentBlock.contents = replaceAtIndex(this.localContentBlock.contents, outer, element);
|
||||
} else {
|
||||
|
|
@ -257,7 +301,7 @@
|
|||
save(contentBlock) {
|
||||
this.$emit('save', contentBlock);
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
</script>
|
||||
|
|
@ -287,12 +331,21 @@
|
|||
}
|
||||
|
||||
&__segment {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
||||
margin-bottom: $large-spacing;
|
||||
|
||||
:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__actions {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
&__content {
|
||||
grid-area: content;
|
||||
overflow-x: visible;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,26 @@
|
|||
<template>
|
||||
<div class="content-element">
|
||||
<!-- Element Chooser if element has chooser type or no type -->
|
||||
<content-block-element-chooser-widget
|
||||
:class="['content-element__component', 'content-element__chooser']"
|
||||
v-bind="element"
|
||||
v-if="isChooser"
|
||||
|
||||
@change-type="changeType"
|
||||
/>
|
||||
<!-- Content Forms -->
|
||||
<content-form-section
|
||||
:title="title"
|
||||
:has-actions="true"
|
||||
:icon="icon"
|
||||
v-else
|
||||
@top="$emit('top')"
|
||||
@up="$emit('up')"
|
||||
@down="$emit('down')"
|
||||
@bottom="$emit('bottom')"
|
||||
@remove="$emit('remove')"
|
||||
>
|
||||
<div class="content-element__section">
|
||||
<!-- Form depending on type of element -->
|
||||
<component
|
||||
:class="['content-element__component']"
|
||||
v-bind="element"
|
||||
|
|
@ -28,27 +36,6 @@
|
|||
@assignment-change-title="changeAssignmentTitle"
|
||||
@assignment-change-assignment="changeAssignmentAssignment"
|
||||
/>
|
||||
|
||||
<a
|
||||
class="contents-form__remove icon-button"
|
||||
@click="$emit('remove')"
|
||||
>
|
||||
<trash-icon
|
||||
class="contents-form__trash-icon icon-button__icon"
|
||||
/>
|
||||
</a>
|
||||
<!-- <a-->
|
||||
<!-- class="button"-->
|
||||
<!-- @click="$emit('up')"-->
|
||||
<!-- >-->
|
||||
<!-- Up-->
|
||||
<!-- </a>-->
|
||||
<!-- <a-->
|
||||
<!-- class="button"-->
|
||||
<!-- @click="$emit('down')"-->
|
||||
<!-- >-->
|
||||
<!-- Down-->
|
||||
<!-- </a>-->
|
||||
</div>
|
||||
</content-form-section>
|
||||
</div>
|
||||
|
|
@ -56,6 +43,7 @@
|
|||
|
||||
<script>
|
||||
import ContentFormSection from '@/components/content-block-form/ContentFormSection';
|
||||
import ContentElementActions from '@/components/content-block-form/ContentElementActions';
|
||||
const TrashIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon');
|
||||
const ContentBlockElementChooserWidget = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/ContentBlockElementChooserWidget');
|
||||
const LinkForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/LinkForm');
|
||||
|
|
@ -77,6 +65,7 @@
|
|||
},
|
||||
|
||||
components: {
|
||||
ContentElementActions,
|
||||
ContentFormSection,
|
||||
TrashIcon,
|
||||
ContentBlockElementChooserWidget,
|
||||
|
|
@ -170,6 +159,9 @@
|
|||
},
|
||||
});
|
||||
},
|
||||
debug(arg) {
|
||||
console.log(arg);
|
||||
},
|
||||
changeUrl(value) {
|
||||
this._updateProperty(value, 'url');
|
||||
},
|
||||
|
|
@ -261,9 +253,18 @@
|
|||
@import '~styles/helpers';
|
||||
|
||||
.content-element {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__actions {
|
||||
display: inline-flex;
|
||||
justify-self: flex-end;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
&__section {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 50px;
|
||||
//grid-template-columns: 1fr 50px;
|
||||
grid-auto-rows: auto;
|
||||
/*width: 95%; // reserve space for scrollbar*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
<template>
|
||||
<div class="content-element-actions">
|
||||
<button
|
||||
class="icon-button"
|
||||
@click.stop="toggle(true)"
|
||||
>
|
||||
<ellipses class="icon-button__icon" />
|
||||
</button>
|
||||
<widget-popover
|
||||
class="content-element-actions__popover"
|
||||
:no-padding="true"
|
||||
v-if="show"
|
||||
@hide-me="toggle(false)"
|
||||
>
|
||||
<section class="content-element-actions__section">
|
||||
<button-with-icon-and-text
|
||||
class="content-element-actions__button"
|
||||
:large="true"
|
||||
icon="arrow-thin-top"
|
||||
text="Ganz nach oben verschieben"
|
||||
@click="emitAndClose('move-top')"
|
||||
/>
|
||||
<button-with-icon-and-text
|
||||
class="content-element-actions__button"
|
||||
:large="true"
|
||||
icon="arrow-thin-up"
|
||||
text="Nach oben verschieben"
|
||||
@click="emitAndClose('move-up')"
|
||||
/>
|
||||
<button-with-icon-and-text
|
||||
class="content-element-actions__button"
|
||||
:large="true"
|
||||
icon="arrow-thin-down"
|
||||
text="Nach unten verschieben"
|
||||
@click="emitAndClose('move-down')"
|
||||
/>
|
||||
<button-with-icon-and-text
|
||||
class="content-element-actions__button"
|
||||
:large="true"
|
||||
icon="arrow-thin-bottom"
|
||||
text="Ganz nach unten verschieben"
|
||||
@click="emitAndClose('move-bottom')"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section class="content-element-actions__section">
|
||||
<button-with-icon-and-text
|
||||
class="content-element-actions__button"
|
||||
:large="true"
|
||||
icon="trash-icon"
|
||||
text="Löschen"
|
||||
@click="emitAndClose('remove')"
|
||||
/>
|
||||
</section>
|
||||
</widget-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WidgetPopover from '@/components/ui/WidgetPopover';
|
||||
import Ellipses from '@/components/icons/Ellipses';
|
||||
import ButtonWithIconAndText from '@/components/ui/ButtonWithIconAndText';
|
||||
|
||||
export default {
|
||||
components: {ButtonWithIconAndText, Ellipses, WidgetPopover},
|
||||
|
||||
data: () => ({
|
||||
show: false,
|
||||
}),
|
||||
|
||||
methods: {
|
||||
toggle(show) {
|
||||
this.show = show;
|
||||
},
|
||||
close() {
|
||||
this.show = false;
|
||||
},
|
||||
up() {
|
||||
this.$emit('move-up');
|
||||
this.close();
|
||||
},
|
||||
down() {
|
||||
this.$emit('move-down');
|
||||
this.close();
|
||||
},
|
||||
top() {
|
||||
this.$emit('move-top');
|
||||
this.close();
|
||||
},
|
||||
bottom() {
|
||||
this.$emit('move-bottom');
|
||||
this.close();
|
||||
},
|
||||
emitAndClose(event) {
|
||||
this.$emit(event);
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~styles/helpers';
|
||||
|
||||
.content-element-actions {
|
||||
position: relative;
|
||||
|
||||
&__popover {
|
||||
white-space: nowrap;
|
||||
top: 100%;
|
||||
transform: translateY($small-spacing);
|
||||
|
||||
}
|
||||
|
||||
&__section {
|
||||
border-bottom: 1px solid $color-silver-dark;
|
||||
padding: $medium-spacing $small-spacing;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-bottom: $medium-spacing;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -6,12 +6,25 @@
|
|||
:is="icon"
|
||||
/> <span class="content-form-section__title">{{ title }}</span>
|
||||
</h2>
|
||||
<slot />
|
||||
|
||||
<content-element-actions
|
||||
class="content-form-section__actions"
|
||||
v-if="hasActions"
|
||||
@remove="$emit('remove')"
|
||||
@move-top="$emit('top')"
|
||||
@move-up="$emit('up')"
|
||||
@move-down="$emit('down')"
|
||||
@move-bottom="$emit('bottom')"
|
||||
/>
|
||||
<div class="content-form-section__content">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import formElementIcons from '@/components/ui/form-element-icons';
|
||||
import ContentElementActions from '@/components/content-block-form/ContentElementActions';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
@ -22,9 +35,14 @@
|
|||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
hasActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
ContentElementActions,
|
||||
...formElementIcons
|
||||
}
|
||||
};
|
||||
|
|
@ -39,9 +57,23 @@
|
|||
padding: $small-spacing $medium-spacing;
|
||||
margin-bottom: $medium-spacing;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: auto;
|
||||
grid-template-areas: 'h a' 'c c';
|
||||
align-items: center;
|
||||
grid-row-gap: $medium-spacing;
|
||||
|
||||
&__heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
grid-area: h;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
grid-area: a;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
&__title {
|
||||
|
|
@ -54,5 +86,9 @@
|
|||
height: 28px;
|
||||
margin-right: $small-spacing;
|
||||
}
|
||||
|
||||
&__content {
|
||||
grid-area: c;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 100 100"
|
||||
>
|
||||
<path
|
||||
d="M79 80.8H51.6l.1-.1 25.7-25.8c1-1 1-2.6 0-3.5-1-1-2.6-1-3.5 0L52.5 72.9V16.7c0-1.4-1.1-2.5-2.5-2.5s-2.5 1.1-2.5 2.5v56.2L26.1 51.4c-1-1-2.6-1-3.5 0-1 1-1 2.6 0 3.5l25.7 25.8.1.1H21c-1.4 0-2.5 1.1-2.5 2.5s1.1 2.5 2.5 2.5h58c1.4 0 2.5-1.1 2.5-2.5s-1.1-2.5-2.5-2.5z"
|
||||
id="shape"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 100 100"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<path
|
||||
d="M77.5 55.7c-1-1-2.6-1-3.5 0L52.5 77.2V21.1c0-1.4-1.1-2.5-2.5-2.5s-2.5 1.1-2.5 2.5v56.2L26.1 55.8c-1-1-2.6-1-3.5 0-1 1-1 2.6 0 3.5l25.7 25.8c.1.1.2.2.4.3 0 0 .1 0 .1.1.1.1.2.1.3.2h.1c.1 0 .2.1.3.1h1c.1 0 .2-.1.3-.1h.1c.1 0 .2-.1.3-.2 0 0 .1 0 .1-.1.1-.1.3-.2.4-.3l25.7-25.8c1.1-1 1.1-2.6.1-3.6z"
|
||||
id="arrow-thin-down"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100"
|
||||
height="100"
|
||||
viewBox="0 0 100 100"
|
||||
id="shape"
|
||||
>
|
||||
<path
|
||||
d="M79,14.2H21a2.5,2.5,0,0,0,0,5H48.35l-.12.1h0L22.52,45.08a2.5,2.5,0,1,0,3.54,3.53L47.5,27.12V83.3a2.5,2.5,0,1,0,5,0V27.12L73.94,48.61a2.5,2.5,0,1,0,3.54-3.53L51.77,19.3h0l-.12-.1H79a2.5,2.5,0,0,0,0-5Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 100 100"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<path
|
||||
d="M77.5 45.1 51.8 19.3c-.5-.5-1.1-.7-1.8-.7s-1.3.3-1.8.7L22.5 45.1c-1 1-1 2.6 0 3.5 1 1 2.6 1 3.5 0l21.4-21.5v56.2c0 1.4 1.1 2.5 2.5 2.5s2.5-1.1 2.5-2.5V27.1l21.4 21.5c.5.5 1.1.7 1.8.7.6 0 1.3-.2 1.8-.7 1.1-1 1.1-2.5.1-3.5z"
|
||||
id="arrow-thin-up"
|
||||
/></svg>
|
||||
</template>
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
<template>
|
||||
<a class="button-with-icon-and-text">
|
||||
<a
|
||||
:class="[ 'button-with-icon-and-text', {'button-with-icon-and-text--large': large}]"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<component
|
||||
class="button-with-icon-and-text__icon"
|
||||
:is="icon"
|
||||
|
|
@ -9,8 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
const DocumentIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon');
|
||||
const DocumentWithLinesIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentWithLinesIcon');
|
||||
import formElementIcons from '@/components/ui/form-element-icons';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
@ -21,12 +23,15 @@
|
|||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
large: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
DocumentIcon,
|
||||
DocumentWithLinesIcon
|
||||
...formElementIcons
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -48,5 +53,13 @@
|
|||
&__text {
|
||||
@include small-text;
|
||||
}
|
||||
|
||||
$p: &;
|
||||
|
||||
&--large {
|
||||
#{$p}__text {
|
||||
@include large-link;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
class="widget-popover"
|
||||
v-click-outside="hidePopover"
|
||||
>
|
||||
<ul class="widget-popover__links popover-links">
|
||||
<ul :class="['widget-popover__links popover-links', {'popover-links--no-padding': noPadding}]">
|
||||
<slot />
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -16,6 +16,10 @@
|
|||
mobile: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
noPadding: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -26,7 +30,3 @@
|
|||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,19 @@ const TextIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons
|
|||
const SpeechBubbleIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/SpeechBubbleIcon');
|
||||
const DocumentIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon');
|
||||
const TitleIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/TitleIcon');
|
||||
const DocumentWithLinesIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentWithLinesIcon');
|
||||
|
||||
const ArrowThinBottom = () => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinBottom');
|
||||
const ArrowThinDown = () => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinDown');
|
||||
const ArrowThinTop = () => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinTop');
|
||||
const ArrowThinUp = () => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinUp');
|
||||
|
||||
const TrashIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon');
|
||||
|
||||
/*
|
||||
for icons with a single word, leave the *-icon name, to prevent conflicts
|
||||
for others, both is fine: arrow-thin-up-icon or arrow-thin-up
|
||||
*/
|
||||
export default {
|
||||
LinkIcon,
|
||||
VideoIcon,
|
||||
|
|
@ -13,5 +25,11 @@ export default {
|
|||
TextIcon,
|
||||
SpeechBubbleIcon,
|
||||
DocumentIcon,
|
||||
TitleIcon
|
||||
TitleIcon,
|
||||
DocumentWithLinesIcon,
|
||||
ArrowThinBottom,
|
||||
ArrowThinDown,
|
||||
ArrowThinTop,
|
||||
ArrowThinUp,
|
||||
TrashIcon
|
||||
};
|
||||
|
|
|
|||
|
|
@ -42,3 +42,16 @@ export const swapElements = (arr, idx1, idx2)=>{
|
|||
...arr.slice(larger+1)
|
||||
];
|
||||
};
|
||||
export const moveToIndex = (arr, from, to) => {
|
||||
const maxLength = arr.length - 1;
|
||||
if (from < 0 || to < 0 || from > maxLength || to > maxLength) {
|
||||
throw new Error('Index out of bounds');
|
||||
}
|
||||
const element = arr[from];
|
||||
const newArr = [...arr.slice(0, from), ...arr.slice(from+1)];
|
||||
return [
|
||||
...newArr.slice(0, to),
|
||||
element,
|
||||
...newArr.slice(to)
|
||||
];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@
|
|||
flex-direction: column;
|
||||
padding: $small-spacing;
|
||||
|
||||
&--no-padding {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
import {removeAtIndex, replaceAtIndex, insertAtIndex, pushToArray, swapElements} from '@/graphql/immutable-operations';
|
||||
import {
|
||||
insertAtIndex,
|
||||
moveToIndex,
|
||||
pushToArray,
|
||||
removeAtIndex,
|
||||
replaceAtIndex,
|
||||
swapElements,
|
||||
} from '@/graphql/immutable-operations';
|
||||
|
||||
describe('Cache operations', () => {
|
||||
it('removes at index', () => {
|
||||
const original = [1, 2, 3];
|
||||
|
|
@ -75,14 +83,14 @@ describe('Cache operations', () => {
|
|||
expect(copy).toEqual([3, 2, 1]);
|
||||
});
|
||||
|
||||
it('swaps neighbors', ()=>{
|
||||
it('swaps neighbors', () => {
|
||||
const original = [1, 2, 3, 4, 5];
|
||||
const copy = swapElements(original, 3, 2);
|
||||
|
||||
expect(copy).toEqual([1, 2, 4, 3, 5]);
|
||||
});
|
||||
|
||||
it('does nothing with wrong indices', ()=>{
|
||||
it('does nothing with wrong indices', () => {
|
||||
const original = [1, 2, 3, 4, 5];
|
||||
const copy1 = swapElements(original, -1, 2);
|
||||
const copy2 = swapElements(original, 1, 99);
|
||||
|
|
@ -97,4 +105,15 @@ describe('Cache operations', () => {
|
|||
expect(copy2.length).toBe(5);
|
||||
expect(copy3.length).toBe(5);
|
||||
});
|
||||
|
||||
it('moves to a specific index', () => {
|
||||
const original = [1, 2, 3, 4, 5, 6, 7];
|
||||
const toStart = moveToIndex(original, 3, 0);
|
||||
const toEnd = moveToIndex(original, 3, original.length - 1);
|
||||
const toMiddle = moveToIndex(original, 3, 2);
|
||||
|
||||
expect(toStart).toEqual([4, 1, 2, 3, 5, 6, 7]);
|
||||
expect(toEnd).toEqual([1, 2, 3, 5, 6, 7, 4]);
|
||||
expect(toMiddle).toEqual([1, 2, 4, 3, 5, 6, 7]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue