11<script setup lang="ts">
2- import { ref , computed , watch , provide } from ' vue' ;
2+ import { ref , computed , provide } from ' vue' ;
33import type { ComponentPublicInstance } from ' vue' ;
44import type {
55 AnyControlNode ,
@@ -17,7 +17,7 @@ import ProgressBar from 'primevue/progressbar';
1717import Button from ' primevue/button' ;
1818
1919const props = defineProps <{ nodes: readonly GeneralChildNode [] }>();
20- const emit = defineEmits ([' endOfForm ' ]);
20+ const emit = defineEmits ([' sendFormFromStepper ' ]);
2121
2222const isGroupNode = (node : GeneralChildNode ): node is GroupNode => {
2323 return node .nodeType === ' group' ;
@@ -59,44 +59,47 @@ const steps = computed(() =>
5959);
6060
6161// Handle stepper state
62- const currentStep = ref (0 );
63- const isCurrentStepValidated = ref (true );
62+ const firstStep = 0 ;
63+ const finalStep = steps .value .length ;
64+ const currentStep = ref (firstStep );
6465const submitPressed = ref (false );
6566provide (' submitPressed' , submitPressed );
6667
67- const validateStep = () => {
68+ const allFieldsValid = () => {
6869 // Manually trigger submitPressed to display error messages
6970 submitPressed .value = true ;
7071
7172 const currentNode = steps .value [currentStep .value ];
73+
74+ // Check group error array
7275 if (isGroupNode (currentNode ) && currentNode .validationState .violations .length > 0 ) {
73- isCurrentStepValidated .value = false ;
76+ return false ;
77+
78+ // Check question single error
7479 } else if (currentNode .validationState .violation ) {
75- isCurrentStepValidated .value = false ;
76- } else {
77- isCurrentStepValidated .value = true ;
80+ return false ;
7881 }
82+
83+ return true ;
7984}
8085const nextStep = () => {
81- validateStep ();
86+ if (! allFieldsValid ()) {
87+ // There was an error validating
88+ return false ;
89+ }
8290
83- if (isCurrentStepValidated .value && currentStep .value < steps .value .length - 1 ) {
91+ // Do not increment further if at end of form
92+ if (currentStep .value < steps .value .length - 1 ) {
8493 // Reset validation triggered later in the form
8594 submitPressed .value = false ;
86- // Also reset validation state of current node
87- isCurrentStepValidated .value = true ;
8895 currentStep .value ++ ;
8996 }
9097};
9198const prevStep = () => {
92- if (currentStep .value > 0 ) {
99+ if (currentStep .value > firstStep ) {
93100 currentStep .value -- ;
94101 }
95102};
96- const isLastStep = computed (() => currentStep .value === steps .value .length - 1 );
97- watch (isLastStep , (newValue ) => {
98- emit (' endOfForm' , newValue );
99- });
100103
101104// // Calculate stepper progress
102105// const totalNodes = computed(() =>
@@ -137,19 +140,55 @@ watch(isLastStep, (newValue) => {
137140 <ExpectModelNode v-else :node =" step" />
138141 </template >
139142 </div >
140-
141- <div class =" navigation-buttons" >
142- <Button label =" Previous" @click =" prevStep" :disabled =" currentStep === 0" />
143- <Button label =" Next" @click =" nextStep" :disabled =" isCurrentStepValidated && currentStep === steps.length - 1" />
144- </div >
145143 </div >
144+
145+ <div class =" navigation-button-group" >
146+ <!-- If swapping to arrows: 🡨 🡪 -->
147+ <Button v-if =" currentStep > firstStep" class =" navigation-button" label =" Back" @click =" prevStep" rounded outlined />
148+ <Button v-if =" currentStep === finalStep" class =" navigation-button" label =" Send" @click =" allFieldsValid ? emit('sendFormFromStepper') : null" rounded />
149+ <!-- Note the button ordering is important here as we use a last-child selector for styling -->
150+ <Button v-if =" currentStep < finalStep" class =" navigation-button" label =" Next" @click =" nextStep" rounded outlined />
151+ </div >
146152</template >
147153
148154<style scoped lang="scss">
149- .navigation-buttons {
155+ .stepper-container {
150156 display : flex ;
151- justify-content : space-between ;
157+ flex-direction : column ;
158+ flex-grow : 1 ;
159+ overflow-y : auto ;
160+ padding-bottom : 3rem ;
161+
162+ :deep (.p-panel ) {
163+ box-shadow : none ;
164+ }
165+ }
166+
167+ .navigation-button-group {
168+ display : flex ;
169+ position : fixed ;
170+ bottom : 0 ;
171+ left : 0 ;
152172 width : 100% ;
153- margin-top : 1rem ;
173+ background : white ;
174+ padding : 1rem ;
175+ justify-content : space-between ;
176+ box-shadow : 0 -2px 5px rgba (0 , 0 , 0 , 0.1 );
177+ }
178+
179+ /* Ensure Next button is on the right when Back is hidden */
180+ .navigation-button-group .navigation-button :last-child {
181+ margin-left : auto ;
182+ }
183+
184+ /* If only one button is visible, align it to the right */
185+ .navigation-button-group :has (.navigation-button :first-child:last-child ) {
186+ justify-content : flex-end ;
187+ }
188+
189+ .navigation-button {
190+ padding-left : 3rem ;
191+ padding-right : 3rem ;
192+ font-size : 1rem ;
154193}
155194 </style >
0 commit comments