PluginProbe
The Innovative Form Builder – IvyForms / 0.4
The Innovative Form Builder – IvyForms v0.4
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.4, at frontend/src/views/_components/menu/IvyHeaderButton.vue

298 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 //flex-shrink: 0;
127 transition: background-color 0.3s;
128 font-size: 14px;
129 margin: 0;
130 font-style: normal;
131 font-weight: 500;
132 gap: var(--spacing-01, 4px);
133 flex: 1;
134
135 // Define priorities and their styles
136 $priorities: (
137 'primary': (
138 transparent,
139 var(--map-base-text-0),
140 var(--map-base-brand-o05),
141 var(--map-base-brand-symbol-0),
142 var(--map-base-brand-fill-0),
143 $primitive-white,
144 ),
145 'secondary': (
146 transparent,
147 var(--map-base-text-0),
148 var(--map-base-purple-o05),
149 var(--map-base-purple-symbol-0),
150 var(--map-base-purple-fill-0),
151 $primitive-white,
152 ),
153 'tertiary': (
154 transparent,
155 var(--map-base-text-0),
156 var(--map-base-dusk-o10),
157 var(--map-base-text-0),
158 var(--map-base-dusk-o10),
159 var(--map-base-text-0),
160 ),
161 );
162
163 // Mixin for priority styles
164 @mixin menuStyles($priority, $colors: ()) {
165 $default-bg: nth($colors, 1);
166 $default-text: nth($colors, 2);
167 $hover-bg: nth($colors, 3);
168 $hover-text: nth($colors, 4);
169 $active-bg: nth($colors, 5);
170 $active-text: nth($colors, 6);
171
172 &.priority-#{$priority} {
173 background-color: $default-bg;
174 color: $default-text;
175
176 // Icon
177 .ivyforms-icon {
178 display: contents;
179 fill: $default-text;
180 width: 20px !important;
181 height: 20px;
182 }
183
184 // Hover
185 &:hover,
186 &.is-hover {
187 background-color: $hover-bg;
188 color: $hover-text;
189
190 .ivyforms-icon {
191 fill: $hover-text;
192 }
193 }
194
195 // Active
196 &:active,
197 &.is-active-item {
198 border: none;
199 background-color: $active-bg;
200 color: $active-text !important;
201
202 .ivyforms-icon {
203 fill: $active-text;
204 }
205 }
206
207 // Focus
208 // Hover
209 &:focus,
210 &.is-focus {
211 background-color: $hover-bg;
212 color: $hover-text;
213
214 .ivyforms-icon {
215 fill: $hover-text;
216 }
217 }
218
219 // Default state when not active
220 &:not(.is-active-item) {
221 background-color: $default-bg;
222 color: $default-text;
223
224 .ivyforms-icon {
225 fill: $default-text;
226 }
227 }
228
229 &.type-tonal {
230 &:active,
231 &.is-active-item {
232 background-color: $hover-bg;
233 color: $hover-text !important;
234
235 .ivyforms-icon {
236 fill: $hover-text;
237 }
238 }
239 }
240
241 &.is-disabled {
242 &:active,
243 &.is-active-item {
244 color: $default-text !important;
245 }
246
247 &:hover,
248 &.is-hover,
249 &:focus,
250 &.is-focus {
251 background-color: $default-bg !important;
252 color: $default-text !important;
253 }
254 }
255
256 &.has-coming-soon {
257 margin: 0;
258 }
259 }
260
261 &.priority-tertiary {
262 &.is-active-item,
263 &:active {
264 border-radius: var(--radius-l, 8px);
265 background: var(--map-ground-level-3);
266 box-shadow: 0 4 15 0 rgba(0, 0, 0, 0.05);
267 }
268 }
269 }
270
271 // Generate styles for all priorities
272 @each $priority, $colors in $priorities {
273 @include menuStyles($priority, $colors);
274 }
275
276 // Size specific styles
277 &.size-s {
278 height: 32px;
279 max-height: 32px;
280 }
281
282 &.size-d {
283 height: 40px;
284 max-height: 40px;
285 }
286
287 &.size-l {
288 height: 48px;
289 max-height: 48px;
290 }
291 span {
292 line-height: 20px;
293 }
294 }
295 }
296 }
297 </style>
298