PluginProbe
The Innovative Form Builder – IvyForms / 0.8.2
The Innovative Form Builder – IvyForms v0.8.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 / IvyIndicatorButton.vue

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

218 lines 5.3 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-indicator-button__wrapper"
4 :class="{ 'is-active': isActive, 'is-fab': props.color === 'fab' }"
5 :style="{ background: getBackgroundColor() }"
6 :aria-label="props.ariaLabel || getLabel('indicator_button')"
7 @click="toggleActiveState"
8 @mouseenter="isHovered = true"
9 @mouseleave="isHovered = false"
10 >
11 <IvyIcon :name="currentIcon" :color="getIconColor()" type="outline" size="l"> </IvyIcon>
12 <span
13 v-if="showIndicator"
14 class="ivyforms-indicator-button__indicator"
15 :class="`priority-${props.indicatorPriority}`"
16 @mouseenter="indicatorHovered = true"
17 @mouseleave="indicatorHovered = false"
18 @click.stop="hideIndicator"
19 >
20 <span v-if="showIndicatorX" class="ivyforms-indicator-button__indicator-x">×</span>
21 </span>
22 </button>
23 </template>
24
25 <script setup lang="ts">
26 import { ref, watch, computed } from 'vue'
27 import { useLabels } from '@/composables/useLabels'
28 const { getLabel } = useLabels()
29
30 interface Props {
31 type: 'sort' | 'filter' | 'folder' | 'datepicker' | 'import' | 'export'
32 color?: 'fab' | 'tertiary'
33 indicator?: boolean
34 indicatorPriority?: 'primary' | 'secondary' | 'amber'
35 customHideIndicator?: () => void
36 ariaLabel?: string
37 }
38
39 const props = withDefaults(defineProps<Props>(), {
40 type: 'sort',
41 color: 'tertiary',
42 indicator: false,
43 indicatorPriority: 'secondary',
44 customHideIndicator: undefined,
45 ariaLabel: '',
46 })
47
48 const showIndicator = ref(props.indicator)
49 const isActive = ref(false)
50 const isHovered = ref(false)
51 const indicatorHovered = ref(false)
52
53 // Only show the "x" when the indicator is on and hovered
54 const showIndicatorX = computed(
55 () => showIndicator.value && (isHovered.value || indicatorHovered.value),
56 )
57
58 // Toggle active state
59 const toggleActiveState = () => {
60 isActive.value = !isActive.value
61 }
62
63 // Logic for the indicator
64 const hideIndicator = () => {
65 if (props.customHideIndicator) {
66 props.customHideIndicator()
67 } else {
68 showIndicator.value = false
69 }
70 isActive.value = false
71 // Reset hover states when the indicator is hidden
72 isHovered.value = false
73 indicatorHovered.value = false
74 }
75
76 watch(
77 () => props.indicator,
78 (newValue) => {
79 showIndicator.value = newValue
80 },
81 )
82
83 // Reset hover state when indicator is turned off
84 watch(showIndicator, (val) => {
85 if (!val) {
86 isHovered.value = false
87 indicatorHovered.value = false
88 }
89 })
90
91 const getIconName = (type: Props['type']) => {
92 const icons = {
93 sort: { default: 'sort-default', hover: 'sort-from-top', active: 'sort-from-top' },
94 filter: { default: 'filter', hover: 'filter', active: 'filter' },
95 folder: { default: 'folder', hover: 'folder', active: 'folder' },
96 datepicker: { default: 'calendar-dot', hover: 'calendar-dot', active: 'calendar-dot' },
97 import: { default: 'import', hover: 'import', active: 'import' },
98 export: { default: 'export', hover: 'export', active: 'export' },
99 }
100
101 return isActive.value
102 ? icons[type].active
103 : isHovered.value
104 ? icons[type].hover
105 : icons[type].default
106 }
107
108 const getBackgroundColor = () => {
109 if (props.color === 'tertiary') {
110 return 'var(--map-base-dusk-fill--4)'
111 }
112 return 'var(--map-ground-level-3)'
113 }
114
115 const getIconColor = () => {
116 if (props.color !== 'tertiary') {
117 return isHovered.value || isActive.value
118 ? 'var(--map-base-purple-symbol-0)'
119 : 'var(--map-base-dusk-symbol-2)'
120 }
121 return 'var(--map-base-dusk-symbol-2)'
122 }
123
124 // Computed property for icon name
125 const currentIcon = computed(() => getIconName(props.type))
126 </script>
127
128 <style lang="scss">
129 .ivyforms-indicator-button__wrapper {
130 position: relative;
131 display: flex;
132 height: 40px;
133 width: 40px;
134 align-items: center;
135 padding: 0;
136 align-self: stretch;
137 border-radius: 8px;
138 cursor: pointer;
139 border: none;
140 outline: none;
141
142 &.is-fab {
143 box-shadow: 0 4px 15px 0 rgba(0, 0, 0, 0.05);
144
145 &:hover {
146 .ivyforms-icon__svg {
147 // Global & Arrows
148 &.category-global,
149 &.category-arrows {
150 fill: var(--map-base-purple-fill--1);
151 }
152 }
153 }
154 }
155
156 &:not(.is-fab) {
157 &:hover {
158 position: relative;
159
160 &::after {
161 content: '';
162 position: absolute;
163 top: 0;
164 left: 0;
165 width: 100%;
166 height: 100%;
167 background-color: var(--map-bw-o90);
168 opacity: 0.05;
169 border-radius: inherit;
170 pointer-events: none;
171 }
172 }
173 }
174
175 // Icon
176 .ivyforms-icon {
177 padding: var(--Spacing-sm, 8px) 8px;
178 }
179
180 // Indicator
181 .ivyforms-indicator-button__indicator {
182 width: 12px;
183 height: 12px;
184 position: absolute;
185 top: 1px;
186 right: 1px;
187 border-radius: 50%;
188 display: flex;
189 align-items: center;
190 justify-content: center;
191 background: var(--map-status-success-fill-0);
192 cursor: pointer;
193 font-size: 0;
194
195 &.priority-secondary {
196 background: var(--map-base-purple-fill--1);
197 }
198
199 &.priority-amber {
200 background: var(--map-status-warning-fill-0);
201 }
202
203 .ivyforms-indicator-button__indicator-x {
204 font-size: 10px;
205 font-weight: bold;
206 color: $primitive-white;
207 line-height: 10px;
208 pointer-events: none;
209 user-select: none;
210 font-family: inherit;
211 display: flex;
212 align-items: center;
213 justify-content: center;
214 }
215 }
216 }
217 </style>
218