PluginProbe
The Innovative Form Builder – IvyForms / 0.8
The Innovative Form Builder – IvyForms v0.8
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.8, at frontend/src/views/_components/context-menu/IvyContextMenuActions.vue

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