PluginProbe
The Innovative Form Builder – IvyForms / 0.8.1
The Innovative Form Builder – IvyForms v0.8.1
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 / menu / IvyHeaderButton.vue

IvyHeaderButton.vue in The Innovative Form Builder – IvyForms 0.8.1, at frontend/src/views/_components/menu/IvyHeaderButton.vue

301 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <template>
2 <ElMenuItem
3 :index="name"
4 :route="route"
5 :class="[
6 `size-${size}`,
7 'ivyforms-menu-item',
8 `priority-${priority}`,
9 `type-${type}`,
10 { 'is-active-item': isActive },
11 { 'has-coming-soon': comingSoon },
12 ]"
13 :disabled="disabled || comingSoon"
14 :aria-label="props.ariaLabel || getLabel('header_button')"
15 @click="onClick"
16 >
17 <IvyIcon v-if="iconStart?.name" v-bind="iconStart" />
18
19 <span>
20 {{ label }}
21 </span>
22
23 <IvyIcon v-if="iconEnd?.name" v-bind="iconEnd" />
24
25 <ComingSoonBadge
26 v-if="props.comingSoon"
27 class="ivyforms-header-button-wrapper__coming-soon"
28 :size="'s'"
29 />
30 </ElMenuItem>
31 </template>
32
33 <script setup lang="ts">
34 import { computed } from 'vue'
35 import type { IconCategory } from '@/types/icons/icon-category'
36 import type { IconType } from '@/types/icons/icon-type'
37 import type { IconSize } from '@/types/icons/icon-size'
38 import { useLabels } from '@/composables/useLabels'
39 const { getLabel } = useLabels()
40
41 interface IconConfig {
42 name: string
43 type?: IconType
44 category?: IconCategory
45 size?: IconSize
46 outerSize?: string
47 }
48
49 interface Props {
50 name?: string
51 label: string
52 priority?: 'primary' | 'secondary' | 'tertiary'
53 size?: 's' | 'd' | 'l'
54 type?: 'filled' | 'tonal' | 'white'
55 disabled?: boolean
56 isActive?: boolean
57 iconStartConfig?: IconConfig
58 iconEndConfig?: IconConfig
59 comingSoon?: boolean
60 ariaLabel?: string
61 route?: object
62 }
63
64 // Default icon configuration
65 const defaultIconConfig: IconConfig = {
66 name: 'page',
67 size: 'l',
68 outerSize: '20px',
69 }
70
71 // Merge icon configuration only if the prop is passed
72 const getIconConfig = (iconConfig?: IconConfig) =>
73 iconConfig ? { ...defaultIconConfig, ...iconConfig } : undefined
74
75 const props = withDefaults(defineProps<Props>(), {
76 priority: 'primary',
77 size: 'd',
78 type: 'filled',
79 isActive: false,
80 disabled: false,
81 iconStartConfig: undefined,
82 iconEndConfig: undefined,
83 name: '',
84 comingSoon: false,
85 ariaLabel: '',
86 route: undefined,
87 })
88
89 // Apply defaults only if the config exists
90 const iconStart = computed(() => getIconConfig(props.iconStartConfig))
91 const iconEnd = computed(() => getIconConfig(props.iconEndConfig))
92
93 const emit = defineEmits(['click'])
94
95 const onClick = () => {
96 if (!props.disabled) {
97 emit('click', props.name)
98 }
99 }
100 </script>
101
102 <style lang="scss">
103 @use 'sass:list' as *;
104 @use 'sass:meta' as *;
105
106 // Position the Coming Soon badge correctly
107 .ivyforms-header-button-wrapper__coming-soon {
108 position: absolute;
109 top: -7px;
110 right: -4px;
111 }
112
113 .ivyforms-menu {
114 &.el-menu {
115 // Menu Item
116 .ivyforms-menu-item {
117 position: relative;
118 display: inline-flex;
119 align-items: center;
120 justify-content: center;
121 padding: 10px var(--spacing-04, 16px);
122 border-radius: 8px;
123 border: none;
124 min-width: 44px;
125 height: 40px;
126 font-size: 14px;
127 margin: 0;
128 font-style: normal;
129 font-weight: 500;
130 gap: var(--spacing-01, 4px);
131 flex: 1;
132
133 // Define priorities and their styles
134 $priorities: (
135 'primary': (
136 transparent,
137 var(--map-base-text-0),
138 var(--map-base-brand-o05),
139 var(--map-base-brand-symbol-0),
140 var(--map-base-brand-fill-0),
141 $primitive-white,
142 ),
143 'secondary': (
144 transparent,
145 var(--map-base-text-0),
146 var(--map-base-purple-o05),
147 var(--map-base-purple-symbol-0),
148 var(--map-base-purple-fill-0),
149 $primitive-white,
150 ),
151 'tertiary': (
152 transparent,
153 var(--map-base-text-0),
154 var(--map-base-dusk-o10),
155 var(--map-base-text-0),
156 var(--map-base-dusk-o10),
157 var(--map-base-text-0),
158 ),
159 );
160
161 // Mixin for priority styles
162 @mixin menuStyles($priority, $colors: ()) {
163 $default-bg: nth($colors, 1);
164 $default-text: nth($colors, 2);
165 $hover-bg: nth($colors, 3);
166 $hover-text: nth($colors, 4);
167 $active-bg: nth($colors, 5);
168 $active-text: nth($colors, 6);
169
170 &.priority-#{$priority} {
171 background-color: $default-bg;
172 color: $default-text;
173
174 // Icon
175 .ivyforms-icon {
176 display: contents;
177 fill: $default-text;
178 width: 20px !important;
179 height: 20px;
180 }
181
182 // Hover
183 &:hover,
184 &.is-hover {
185 background-color: $hover-bg;
186 color: $hover-text;
187
188 .ivyforms-icon {
189 fill: $hover-text;
190 }
191 }
192
193 // Active
194 &:active,
195 &.is-active-item {
196 border: none;
197 background-color: $active-bg;
198 color: $active-text !important;
199
200 .ivyforms-icon {
201 fill: $active-text;
202 }
203 }
204
205 &.is-active {
206 color: $active-bg !important;
207 }
208
209 // Focus
210 // Hover
211 &:focus,
212 &.is-focus {
213 background-color: $hover-bg;
214 color: $hover-text;
215
216 .ivyforms-icon {
217 fill: $hover-text;
218 }
219 }
220
221 // Default state when not active
222 &:not(.is-active-item) {
223 background-color: $default-bg;
224 color: $default-text !important;
225 border: none;
226
227 .ivyforms-icon {
228 fill: $default-text;
229 }
230 }
231
232 &.type-tonal {
233 &:active,
234 &.is-active-item {
235 background-color: $hover-bg !important;
236 color: $hover-text !important;
237
238 .ivyforms-icon {
239 fill: $hover-text;
240 }
241 }
242 }
243
244 &.is-disabled {
245 &:active,
246 &.is-active-item {
247 color: $default-text !important;
248 }
249
250 &:hover,
251 &.is-hover,
252 &:focus,
253 &.is-focus {
254 background-color: $default-bg !important;
255 color: $default-text !important;
256 }
257 }
258
259 &.has-coming-soon {
260 margin: 0;
261 }
262 }
263
264 &.priority-tertiary {
265 &.is-active-item,
266 &:active {
267 border-radius: var(--radius-l, 8px);
268 background: var(--map-ground-level-3);
269 box-shadow: 0 4 15 0 rgba(0, 0, 0, 0.05);
270 }
271 }
272 }
273
274 // Generate styles for all priorities
275 @each $priority, $colors in $priorities {
276 @include menuStyles($priority, $colors);
277 }
278
279 // Size specific styles
280 &.size-s {
281 height: 32px;
282 max-height: 32px;
283 }
284
285 &.size-d {
286 height: 40px;
287 max-height: 40px;
288 }
289
290 &.size-l {
291 height: 48px;
292 max-height: 48px;
293 }
294 span {
295 line-height: 20px;
296 }
297 }
298 }
299 }
300 </style>
301