PluginProbe
The Innovative Form Builder – IvyForms / 0.2
The Innovative Form Builder – IvyForms v0.2
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / views / _components / context-menu / IvyContextMenuActions.vue

IvyContextMenuActions.vue in The Innovative Form Builder – IvyForms 0.2, at frontend/src/views/_components/context-menu/IvyContextMenuActions.vue

277 lines 6.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <template>
2 <div
3 v-for="action in props.actions"
4 :key="getLabel(action.label)"
5 class="ivyforms-context-menu-action regular-16"
6 :class="{
7 divided: isDivided(action.divided),
8 secondary: isSecondary(action.secondary),
9 hidden: action?.isHidden?.(),
10 ghost: props.style === 'ghost',
11 active: action?.isActive?.(),
12 'coming-soon': isComingSoon(action.isComingSoon),
13 }"
14 @click="() => handleActionClick(action)"
15 >
16 <div
17 class="ivyforms-context-menu-action__wrapper ivyforms-flex ivyforms-justify-content-between ivyforms-flex-grow ivyforms-gap-8 ivyforms-pr-8"
18 :class="{ danger: isDanger(action.danger) }"
19 >
20 <div
21 class="ivyforms-context-menu-action__wrapper__content ivyforms-flex ivyforms-align-items-center"
22 >
23 <IvyIcon
24 v-if="action.icon"
25 :color="getIconColor(action)"
26 :name="getIcon(action.icon)"
27 :type="action.iconType || 'outline'"
28 :category="action.iconCategory || 'global'"
29 :size="action.iconSize || 'd'"
30 />
31 {{ action.label }}
32 </div>
33 <div
34 v-if="action.rightText"
35 class="ivy-context-menu-action__right-text regular-14 text-muted"
36 >
37 {{ action.rightText }}
38 </div>
39 <ComingSoonBadge v-if="isComingSoon(action.isComingSoon)" />
40 </div>
41 </div>
42 </template>
43
44 <script setup lang="ts">
45 import { defineEmits } from 'vue'
46 import type { ContextMenuAction } from './context-menu-action'
47 interface Props {
48 actions: ContextMenuAction[]
49 entityId?: number | null
50 style?: 'ghost' | 'filled'
51 }
52
53 const props = withDefaults(defineProps<Props>(), {
54 actions: () => [],
55 entityId: null,
56 style: 'filled',
57 })
58
59 const emit = defineEmits(['confirm-delete', 'action-executed', 'close-menu'])
60
61 const handleActionClick = (action: ContextMenuAction) => {
62 if (isComingSoon(action.isComingSoon)) {
63 return
64 }
65
66 if (action.handler) {
67 action.handler(props.entityId)
68 }
69
70 emit('action-executed')
71 }
72
73 const getIconColor = (action: ContextMenuAction) => {
74 if (isDanger(action.danger)) {
75 return 'var(--map-status-error-symbol-0)'
76 }
77
78 if (action.iconColor) {
79 return action.iconColor
80 }
81
82 return 'var(--map-base-dusk-symbol-2)'
83 }
84
85 const getLabel = (label: string | ((entityId: number | null) => string)) => {
86 if (typeof label === 'function') {
87 return label(props.entityId)
88 }
89 return label
90 }
91
92 const getIcon = (icon: string | ((entityId: number | null) => string)) => {
93 if (typeof icon === 'function') {
94 return icon(props.entityId)
95 }
96 return icon
97 }
98
99 const isDanger = (danger: (() => boolean) | boolean | undefined) => {
100 if (typeof danger === 'function') {
101 return danger()
102 }
103 return danger
104 }
105
106 const isDivided = (divided: (() => boolean) | boolean | undefined) => {
107 if (typeof divided === 'function') {
108 return divided()
109 }
110 return divided
111 }
112
113 const isSecondary = (secondary: (() => boolean) | boolean | undefined) => {
114 if (typeof secondary === 'function') {
115 return secondary()
116 }
117 return secondary
118 }
119
120 const isComingSoon = (isComingSoon: (() => boolean) | boolean | undefined) => {
121 if (typeof isComingSoon === 'function') {
122 return isComingSoon()
123 }
124 return isComingSoon
125 }
126 </script>
127
128 <style lang="scss">
129 // Context Menu Action
130 .ivyforms-context-menu-action {
131 width: 100%;
132 display: flex;
133 // Divided
134 &.divided {
135 border-top: 1px solid var(--map-divider);
136 padding-top: 2px;
137 margin-top: 2px;
138 }
139
140 // Hidden
141 &.hidden {
142 display: none;
143 }
144
145 // Secondary
146 &.secondary:not(.coming-soon) {
147 // Active
148 &:active,
149 &.active {
150 .ivyforms-context-menu-action__wrapper {
151 background-color: var(--map-base-purple-o05);
152 color: var(--map-base-purple-symbol-0);
153 font-weight: 400;
154
155 .ivyforms-icon {
156 svg {
157 fill: var(--map-base-purple-symbol-0);
158 }
159 }
160 }
161 }
162 }
163
164 // Active
165 &.active:not(.coming-soon) {
166 .ivyforms-context-menu-action__wrapper {
167 background-color: var(--map-base-brand-o05);
168 color: var(--map-base-brand-symbol-0);
169 font-weight: 400;
170
171 .ivyforms-icon {
172 svg {
173 fill: var(--map-base-brand-symbol-0);
174 }
175 }
176 }
177 }
178 &:active:not(.coming-soon) {
179 > .ivyforms-context-menu-action__wrapper {
180 background-color: var(--map-base-brand-o05);
181 color: var(--map-base-brand-symbol-0);
182 font-weight: 400;
183
184 .ivyforms-icon {
185 svg {
186 fill: var(--map-base-brand-symbol-0);
187 }
188 }
189
190 &.danger {
191 color: var(--map-status-error-symbol-0);
192 background-color: var(--map-status-error-o05);
193
194 .ivyforms-icon {
195 svg {
196 fill: var(--map-status-error-symbol-0);
197 }
198 }
199 }
200 }
201 }
202
203 // Ghost
204 &.style-ghost {
205 }
206
207 // Wrapper
208 &__wrapper {
209 padding: 8px 16px;
210 cursor: pointer;
211 display: flex;
212 align-items: center;
213 margin: 0;
214 height: 36px;
215 color: var(--map-base-text-0);
216
217 // Tablet & Up
218 @include tablet-and-up {
219 height: 36px;
220 margin: 0;
221 padding: 0 0 0 8px;
222 font-size: 14px;
223 line-height: 20px;
224 }
225
226 // Hover
227 &:hover {
228 background-color: var(--map-hover);
229 }
230
231 // Danger
232 &.danger {
233 color: var(--map-status-error-symbol-0);
234
235 // Active
236 &:active:not(.coming-soon),
237 &.active:not(.coming-soon) {
238 background: var(--map-status-error-o05);
239 // Hover
240 &:hover {
241 background: var(--map-status-error-o10);
242 }
243 }
244 }
245
246 // Content
247 &__content {
248 gap: 8px;
249 justify-content: space-between;
250
251 // Tablet & Up
252 @include tablet-and-up {
253 gap: 4px;
254 }
255 }
256 }
257
258 &.coming-soon {
259 cursor: not-allowed;
260 justify-content: space-between;
261
262 .ivyforms-context-menu-action__wrapper {
263 cursor: not-allowed;
264 padding: 0 8px;
265
266 &:hover {
267 background-color: unset;
268 }
269
270 .ivyforms-context-menu-action__wrapper__content {
271 opacity: 0.5;
272 }
273 }
274 }
275 }
276 </style>
277