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

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