PluginProbe
The Innovative Form Builder – IvyForms / 0.1.2
The Innovative Form Builder – IvyForms v0.1.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 / button / IvyButtonOption.vue

IvyButtonOption.vue in The Innovative Form Builder – IvyForms 0.1.2, at frontend/src/views/_components/button/IvyButtonOption.vue

619 lines 14.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <template>
2 <button
3 class="ivyforms-button-option"
4 :disabled="props.disabled || props.loading"
5 :class="[
6 'ivyforms-button-option__priority__' + props.priority,
7 'ivyforms-button-option__type__' + props.type,
8 'ivyforms-button-option__size__' + props.size,
9 {
10 'is-active': props.active,
11 'is-loading': props.loading,
12 'is-icon-start': props.iconStart && $slots.default && !props.iconOnly,
13 'is-icon-end': props.iconEnd && $slots.default && !props.iconOnly,
14 'is-icon-only': ((props.iconStart || props.iconEnd) && !$slots.default) || props.iconOnly,
15 },
16 ]"
17 :style="{ width: props.fullWidth ? '100%' : undefined }"
18 @click="handleClick"
19 @mousedown="handleMouseDown"
20 >
21 <span v-if="props.loading" class="ivyforms-button-option__spinner-container">
22 <svg viewBox="0 0 50 50" class="ivyforms-button-option__spinner">>
23 <circle class="ivyforms-button-option__spinner__line" cx="25" cy="25" r="22.5" />
24 </svg>
25 </span>
26 <IvyIcon
27 v-if="props.iconStart && !props.loading"
28 :name="props.iconStart"
29 :category="props.iconStartCategory"
30 :type="props.iconStartType"
31 :size="props.size"
32 class="ivyforms-button-option__icon-start"
33 />
34 <span v-if="$slots.default && !props.iconOnly">
35 <slot />
36 </span>
37 <IvyIcon
38 v-if="props.iconEnd"
39 :name="props.iconEnd"
40 :category="props.iconEndCategory"
41 :type="props.iconEndType"
42 :size="props.size"
43 class="ivyforms-button-option__icon-end"
44 />
45 <svg class="ivyforms-button-option__transition" width="100%" height="100%" fill="none">>
46 <g>
47 <rect width="100%" height="100%" />
48 </g>
49 </svg>
50 </button>
51 </template>
52
53 <script setup lang="ts">
54 import type { IconCategory } from '@/types/icons/icon-category'
55 import type { IconType } from '@/types/icons/icon-type'
56
57 interface Props {
58 priority?:
59 | 'primary'
60 | 'secondary'
61 | 'tertiary'
62 | 'success'
63 | 'warning'
64 | 'danger'
65 | 'white'
66 | 'shadow-white'
67 type?: 'fill' | 'border' | 'ghost'
68 size?: 'l' | 'd' | 's' | 'xs'
69 disabled?: boolean
70 loading?: boolean
71 active?: boolean
72 iconStart?: string
73 iconEnd?: string
74 iconStartCategory?: IconCategory
75 iconEndCategory?: IconCategory
76 iconStartType?: IconType
77 iconEndType?: IconType
78 fullWidth?: boolean
79 iconOnly?: boolean
80 }
81
82 const props = withDefaults(defineProps<Props>(), {
83 priority: 'primary',
84 type: 'fill',
85 size: 'd',
86 disabled: false,
87 loading: false,
88 active: false,
89 iconStart: '', // Default value for iconStart
90 iconEnd: '', // Default value for iconEnd
91 iconStartCategory: 'global',
92 iconEndCategory: 'global',
93 iconStartType: 'fill',
94 iconEndType: 'fill',
95 fullWidth: false,
96 iconOnly: false,
97 })
98
99 const handleClick = () => {
100 ;(document?.activeElement as HTMLElement).blur()
101 }
102
103 const handleMouseDown = (event: Event) => {
104 event.preventDefault()
105 }
106 </script>
107
108 <style lang="scss">
109 @use 'sass:list' as *;
110 @use 'sass:meta' as *;
111
112 $icon-start: '.ivyforms-button-option__icon-start';
113 $icon-end: '.ivyforms-button-option__icon-end';
114 $transition: '.ivyforms-button-option__transition';
115 $type-fill: '.ivyforms-button-option__type__fill';
116 $type-border: '.ivyforms-button-option__type__border';
117 $type-ghost: '.ivyforms-button-option__type__ghost';
118
119 // Button Option
120 .ivyforms-button-option {
121 position: relative;
122 border-radius: 8px;
123 display: inline-flex;
124 align-items: center;
125 white-space: nowrap;
126 justify-content: center;
127 flex-shrink: 0;
128 -webkit-tap-highlight-color: transparent;
129 user-select: none;
130 outline: none;
131 border: none;
132 cursor: pointer;
133 font-weight: 500;
134 max-width: 100%;
135
136 // Everything except transition
137 > *:not(#{$transition}) {
138 z-index: 1;
139 }
140
141 // Transition
142 & #{$transition} {
143 position: absolute;
144 top: 0;
145 left: 0;
146 width: 100%;
147 height: 100%;
148 border-radius: 8px;
149 fill: transparent;
150 opacity: 0;
151 }
152
153 // Hover
154 @media (hover: hover) and (pointer: fine) {
155 &:hover:not(:focus):not(:disabled):not(:active) {
156 // Transition
157 & #{$transition} {
158 opacity: 0.05;
159 }
160
161 // Ghost and Border types for all priorities except secondary
162 &:not(.ivyforms-button-option__priority__secondary) {
163 &#{$type-ghost},
164 &#{$type-border} {
165 & #{$transition} {
166 opacity: 0.08;
167 }
168 }
169 }
170 }
171 }
172
173 // Active
174 &.is-active:not(:disabled),
175 &:active:not(:disabled),
176 &[aria-expanded='true']='true'] {
177 // Transition
178 & #{$transition} {
179 opacity: 0.16;
180 fill: var(--map-ground-level-2-background);
181 }
182 }
183
184 &.is-active {
185 .ivyforms-icon {
186 svg {
187 fill: var(--map-base-purple-symbol-0);
188 stroke: var(--map-base-purple-symbol-0);
189 }
190 }
191 }
192
193 // Focus
194 &:focus:not(:active) {
195 // Transition
196 & #{$transition} {
197 opacity: 0.4;
198 }
199 }
200
201 // Disabled & Loading
202 &:disabled,
203 &.is-loading {
204 opacity: 0.5;
205 }
206
207 // Priorities
208 $priorities: (
209 'primary': (
210 var(--map-base-brand-fill-0),
211 var(--primitive-white),
212 var(--map-base-brand-stroke-0),
213 var(--map-base-brand-fill-4),
214 var(--map-base-brand-fill-0),
215 var(--map-base-brand-symbol-0),
216 ),
217 'secondary': (
218 var(--map-base-purple-fill-0),
219 var(--primitive-white),
220 var(--map-base-purple-stroke-0),
221 var(--map-base-purple-fill-4),
222 var(--map-base-purple-fill-0),
223 var(--map-base-purple-symbol-0),
224 ),
225 'tertiary': (
226 var(--map-base-dusk-fill--4),
227 var(--map-base-dusk-symbol-2),
228 var(--map-base-dusk-stroke-0),
229 var(--map-base-dusk-fill-0),
230 var(--map-base-dusk-fill-4),
231 var(--map-base-dusk-symbol-2),
232 ),
233 'success': (
234 var(--map-status-success-fill-0),
235 var(--primitive-white),
236 var(--map-status-success-stroke-0),
237 var(--map-status-success-fill-4),
238 var(--map-status-success-fill-0),
239 var(--map-status-success-symbol-0),
240 ),
241 'warning': (
242 var(--map-status-warning-fill-0),
243 var(--primitive-white),
244 var(--map-status-warning-stroke-0),
245 var(--map-status-warning-fill-4),
246 var(--map-status-warning-fill-0),
247 var(--map-status-warning-symbol-0),
248 ),
249 'danger': (
250 var(--map-status-error-fill-0),
251 var(--primitive-white),
252 var(--map-status-error-stroke-0),
253 var(--map-status-error-fill-4),
254 var(--map-status-error-fill-0),
255 var(--map-status-error-symbol-0),
256 ),
257 'white': (
258 var(--primitive-white),
259 var(--primitive-black),
260 var(--primitive-white),
261 var(--primitive-white),
262 var(--map-bw-o90),
263 var(--map-bw),
264 ),
265 'shadow-white': (
266 var(--map-ground-level-2-background),
267 var(--map-base-dusk-symbol-2),
268 var(--primitive-white),
269 var(--primitive-white),
270 var(--primitive-white),
271 var(--map-base-dusk-symbol-2),
272 ),
273 );
274
275 // Priority Styles
276 @mixin priorityStyles($priority, $colors: ()) {
277 $fill-color: nth($colors, 1);
278 $text-color: nth($colors, 2);
279 $border-focus-color: nth($colors, 3);
280 $hover-fill-fill: nth($colors, 4);
281 $hover-fill-border-ghost: if(length($colors) >= 5, nth($colors, 5), nth($colors, 4));
282 $text-color-border-ghost: if(length($colors) >= 6, nth($colors, 6), nth($colors, 2));
283
284 // Priority
285 &__priority__#{$priority} {
286 color: $text-color;
287 --spinner-line-color: #{$text-color};
288 @if $priority == 'shadow-white' {
289 box-shadow: 0px 4px 15px 0px rgba(0, 0, 0, 0.05);
290 }
291
292 // Icon
293 #{$icon-start},
294 #{$icon-end} {
295 fill: $text-color;
296 stroke: $text-color;
297 }
298
299 // Focus
300 &:focus:not(:active) {
301 & .ivyforms-button-option__transition {
302 box-shadow: 0 0 0 4px $border-focus-color;
303 }
304 }
305
306 // Fill
307 &.ivyforms-button-option__type__fill {
308 background-color: $fill-color;
309
310 // Hover
311 @media (hover: hover) and (pointer: fine) {
312 &:hover:not(:focus):not(:disabled):not(:active) {
313 & .ivyforms-button-option__transition {
314 fill: $hover-fill-fill;
315 }
316 }
317 }
318 }
319
320 // Border & Ghost
321 &.ivyforms-button-option__type__border,
322 &.ivyforms-button-option__type__ghost {
323 color: $text-color-border-ghost;
324 --spinner-line-color: #{$text-color-border-ghost};
325
326 @if $priority == 'shadow-white' {
327 box-shadow: none;
328 }
329
330 // Hover
331 @media (hover: hover) and (pointer: fine) {
332 &:hover:not(:focus):not(:disabled):not(:active) {
333 & .ivyforms-button-option__transition {
334 fill: $hover-fill-border-ghost;
335 }
336 }
337 }
338
339 // Primary
340 &.ivyforms-button-option__priority__primary {
341 // Icon
342 #{$icon-start},
343 #{$icon-end} {
344 fill: var(--map-base-brand-symbol-0);
345 }
346 }
347
348 // Secondary
349 &.ivyforms-button-option__priority__secondary {
350 // Icon
351 #{$icon-start},
352 #{$icon-end} {
353 fill: var(--map-base-purple-symbol-0);
354 }
355 }
356 // Success
357 &.ivyforms-button-option__priority__success {
358 // Icon
359 #{$icon-start},
360 #{$icon-end} {
361 fill: var(--map-base-brand-symbol-0);
362 }
363 }
364 // Warning
365 &.ivyforms-button-option__priority__warning {
366 // Icon
367 #{$icon-start},
368 #{$icon-end} {
369 fill: var(--map-status-warning-symbol-0);
370 }
371 }
372 // Danger
373 &.ivyforms-button-option__priority__danger {
374 // Icon
375 #{$icon-start},
376 #{$icon-end} {
377 fill: var(--map-status-error-symbol-0);
378 }
379 }
380 // White
381 &.ivyforms-button-option__priority__white {
382 color: var(--primitive-white);
383 // Icon
384 #{$icon-start},
385 #{$icon-end} {
386 fill: var(--map-base-dusk-symbol-2);
387 }
388 }
389 // Shadow White
390 &.ivyforms-button-option__priority__shadow-white {
391 color: var(--primitive-white);
392 // Icon
393 #{$icon-start},
394 #{$icon-end} {
395 fill: var(--primitive-white);
396 }
397 }
398
399 // Border
400 &.ivyforms-button-option__type__border {
401 border: 1px solid $border-focus-color;
402
403 // Focus
404 &:focus:not(:active) {
405 // Transition
406 & #{$transition} {
407 box-shadow: 0 0 0 5px $border-focus-color;
408 }
409 }
410 }
411 }
412 }
413 }
414
415 @each $priority, $colors in $priorities {
416 @include priorityStyles($priority, $colors);
417 }
418
419 // Type
420 &__type {
421 // Border
422 &__border {
423 background-color: transparent;
424 }
425
426 // Ghost
427 &__ghost {
428 background-color: transparent;
429 }
430 }
431
432 // Define sizes in a map
433 $sizes: (
434 'l': (
435 48px,
436 16px,
437 normal,
438 (10px, 24px, 10px, 24px),
439 // padding
440 (10px, 24px, 10px, 18px),
441 // padding with icon on start
442 (10px, 18px, 10px, 24px),
443 //padding with icon on end
444 8px,
445 2px,
446 ),
447 'd': (
448 40px,
449 16px,
450 normal,
451 (8px, 16px, 8px, 16px),
452 // padding
453 (8px, 16px, 8px, 10px),
454 // padding with icon on start
455 (8px, 10px, 8px, 16px),
456 //padding with icon on end
457 8px,
458 2px,
459 ),
460 's': (
461 36px,
462 14px,
463 normal,
464 (4px, 12px, 4px, 12px),
465 // padding
466 (4px, 12px, 4px, 6px),
467 // padding with icon on start
468 (4px, 6px, 4px, 12px),
469 //padding with icon on end
470 8px,
471 2px,
472 ),
473 'xs': (
474 32px,
475 12px,
476 normal,
477 (0, 8px, 0, 8px),
478 // padding
479 (0, 8px, 0, 4px),
480 // padding with icon on start
481 (0, 4px, 0, 8px),
482 //padding with icon on end
483 4px,
484 4px,
485 ),
486 );
487
488 // Function to process each size value if is array
489 @function process-values($values) {
490 $processed: ();
491 @each $value in $values {
492 // Check if the item is a list (this is where padding array could be)
493 @if type-of($value) == 'list' {
494 $processed: append($processed, join($value, ', ')); // Convert list to string
495 } @else {
496 $processed: append($processed, $value);
497 }
498 }
499 @return $processed;
500 }
501
502 // Iterate over each size to apply styles
503 @each $size, $values in $sizes {
504 &__size__#{$size} {
505 height: nth($values, 1);
506 font-size: nth($values, 2);
507 line-height: nth($values, 3);
508 gap: nth($values, 7);
509 padding: process-values(nth($values, 4));
510
511 // For icon only buttons
512 &.is-icon-start {
513 padding: process-values(nth($values, 5));
514 }
515
516 // For icon only buttons
517 &.is-icon-end {
518 padding: process-values(nth($values, 6));
519 }
520
521 // For icon only buttons
522 &.is-icon-only {
523 width: nth($values, 1);
524 padding: 0;
525
526 // Adjust spinner container for icon only buttons
527 .ivyforms-button-option__spinner-container {
528 margin: 0;
529 }
530 }
531
532 // Spinner
533 .ivyforms-button-option__spinner {
534 padding: nth($values, 8);
535 }
536 }
537 }
538
539 // Icon Containers
540 #{$icon-start},
541 #{$icon-end} {
542 display: flex;
543 align-items: center;
544 justify-content: center;
545 flex-shrink: 0;
546 }
547
548 // Disabled & Loading
549 &:disabled,
550 &.is-loading {
551 cursor: not-allowed;
552 }
553
554 // Text
555 span {
556 display: inline-block;
557 text-overflow: ellipsis;
558 overflow: hidden;
559 white-space: nowrap;
560 }
561
562 // Icon
563 i {
564 display: inline-block;
565 }
566
567 // Spinner Container
568 &__spinner-container {
569 width: 24px;
570 height: 24px;
571 display: flex;
572 align-items: center;
573 justify-content: center;
574 }
575
576 // Spinner
577 &__spinner {
578 --spinner-size: 5;
579 --spinner-ring-size: 5;
580
581 display: block;
582 font-size: calc(var(--spinner-size) * 1em);
583 border-radius: 50%;
584
585 // Spinner Line
586 &__line {
587 fill: none;
588 stroke: var(--spinner-line-color);
589 stroke-width: var(--spinner-ring-size);
590 stroke-linecap: round;
591 transform-origin: 50% 50%;
592 transform: rotate3d(0, 0, 1, 0deg);
593 animation:
594 2156ms spinner-arc ease-in-out infinite,
595 1829ms spinner-rotate linear infinite;
596 }
597 }
598
599 // Loader Animation Rotate
600 @keyframes spinner-rotate {
601 to {
602 transform: rotate3d(0, 0, 1, 360deg);
603 }
604 }
605
606 // Loader Animation Arc
607 @keyframes spinner-arc {
608 from {
609 stroke-dasharray: 0 150;
610 stroke-dashoffset: 0;
611 }
612 to {
613 stroke-dasharray: 100 150;
614 stroke-dashoffset: -140;
615 }
616 }
617 }
618 </style>
619