Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add confirm-route-leave composable #5121

Merged
merged 10 commits into from
Dec 2, 2024
Merged
8 changes: 7 additions & 1 deletion apps/web/src/common/components/modals/ConfirmBackModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
import DeleteModal from '@/common/components/modals/DeleteModal.vue';

const props = defineProps<{
visible: boolean
visible: boolean;
hideCloseButton?: boolean;
}>();
const emit = defineEmits<{(e: 'update:visible', value: boolean): void;
(e: 'confirm'): void;
(e: 'close'): void;
(e: 'cancel'): void;
}>();
const handleClickConfirm = () => {
emit('update:visible', false);
Expand All @@ -18,7 +21,10 @@ const handleClickConfirm = () => {
:header-title="$t('COMMON.CONFIRM_BACK_MODAL.TITLE')"
:visible="props.visible"
:contents="$t('COMMON.CONFIRM_BACK_MODAL.CANNOT_CANCEL')"
:hide-close-button="props.hideCloseButton"
@update:visible="emit('update:visible', $event)"
@confirm="handleClickConfirm"
@close="emit('close')"
@cancel="emit('cancel')"
/>
</template>
5 changes: 5 additions & 0 deletions apps/web/src/common/components/modals/DeleteModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
:hide-footer="hideFooter"
theme-color="alert"
:loading="loading"
:hide-header-close-button="hideCloseButton"
@confirm="handleConfirm"
@close="$emit('close')"
@cancel="$emit('cancel')"
Expand Down Expand Up @@ -96,6 +97,10 @@ export default {
type: Boolean,
default: false,
},
hideCloseButton: {
type: Boolean,
default: false,
},
},
setup(props, { emit }: SetupContext) {
const state = reactive({
Expand Down
52 changes: 52 additions & 0 deletions apps/web/src/common/composables/confirm-route-leave/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { Ref } from 'vue';
import {
ref, readonly,
} from 'vue';
import type { Route } from 'vue-router';
import { useRouter } from 'vue-router/composables';

export const useConfirmRouteLeave = ({
passConfirmation,
}: {
passConfirmation?: Ref<boolean>
} = {}) => {
const router = useRouter();

const isConfirmLeaveModalVisible = ref(false);
const isConfirmed = ref(false);
let nextRoute: Route|undefined;

const openConfirmBackModal = () => {
isConfirmLeaveModalVisible.value = true;
};
const confirmRouteLeave = () => {
isConfirmed.value = true;
isConfirmLeaveModalVisible.value = false;
router.push(nextRoute);
};
const stopRouteLeave = () => {
isConfirmLeaveModalVisible.value = false;
nextRoute = undefined;
};

const handleBeforeRouteLeave = (to, from, next) => {
if (passConfirmation?.value) {
next();
return;
}
if (!isConfirmed.value) {
nextRoute = to;
openConfirmBackModal();
next(false);
} else {
next();
}
};

return {
isConfirmLeaveModalVisible: readonly(isConfirmLeaveModalVisible),
confirmRouteLeave,
stopRouteLeave,
handleBeforeRouteLeave,
};
};
12 changes: 7 additions & 5 deletions apps/web/src/common/composables/go-back/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
import type { Location } from 'vue-router';

import { SpaceRouter } from '@/router';
import { useRouter } from 'vue-router/composables';

export const useGoBack = (mainRoute: Location) => {
const router = useRouter();

let pathFrom;
const setPathFrom = (path: Location) => {
pathFrom = path;
};

const handleClickBackButton = () => {
if (!pathFrom?.name) { // in case of direct access from the other site, go to the main page
SpaceRouter.router.push(mainRoute).catch(() => {});
router.push(mainRoute).catch(() => {});
} else if (pathFrom.name === mainRoute.name) { // in case of access from the service main page in the same site, go to the previous page
SpaceRouter.router.push(pathFrom).catch(() => {});
router.push(pathFrom).catch(() => {});
} else { // in case of access from the other page in the same site, go to the main page
SpaceRouter.router.push(mainRoute).catch(() => {});
router.push(mainRoute).catch(() => {});
}
};

return {
setPathFrom,
handleClickBackButton,
goBack: handleClickBackButton,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ import * as PFieldGroupStories from './PFieldGroup.stories';

<br/>

## No Spacing
<Canvas of={PFieldGroupStories.NoSpacing} />

<br/>

## Playground
<Canvas of={PFieldGroupStories.Playground} />
<Controls of={PFieldGroupStories.Playground} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const Template: Story = {
:help-text="helpText" :required="required"
:label="label"
:style-type="styleType"
:no-spacing="noSpacing"
>
<template v-if="labelSlot" #label>
<div v-html="labelSlot"/>
Expand Down Expand Up @@ -135,6 +136,32 @@ export const StyleType: Story = {
}),
};

export const NoSpacing: Story = {
render: () => ({
components: { PFieldGroup, PTextInput },
template: `
<div>
<p-field-group label="Primary" style-type="primary" no-spacing>
<p-text-input value="Wanjin"/>
</p-field-group>
<br/>
<p-field-group label="Secondary" style-type="secondary" no-spacing>
<p-text-input value="Wanjin"/>
</p-field-group>
<br/>
<p-field-group label="Invalid Text" invalid invalid-text="name is required field." no-spacing>
<p-text-input placeholder="Name" invalid value=""/>
</p-field-group>
<br/>
<p-field-group label="Valid Text" valid valid-text="this is an appropriate name." no-spacing>
<p-text-input placeholder="Name" value="Wanjin"/>
</p-field-group>
<br/>
</div>
`,
}),
};

export const Playground: Story = {
...Template,
};
35 changes: 27 additions & 8 deletions packages/mirinae/src/controls/forms/field-group/PFieldGroup.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<template>
<div class="p-field-group"
:class="[styleType]"
:class="{[styleType]: true, 'no-spacing': noSpacing}"
>
<div class="field-title-box">
<p-field-title v-if="label || $scopedSlots.label"
class="form-label"
:class="{'no-spacing': noSpacing}"
:size="styleType === 'primary' ? 'md' : 'sm'"
:color="styleType === 'primary' ? 'dark' : 'gray'"
@click="$emit('click-field-title')"
Expand All @@ -14,12 +15,14 @@
</slot>
<span v-if="!required"
class="optional-mark"
:class="{'no-spacing': noSpacing}"
>({{ $t('COMPONENT.FIELD_GROUP.OPTIONAL') }})</span>
<slot name="label-extra" />
</p-field-title>
</div>
<small v-if="$scopedSlots.help || helpText"
class="help-msg"
:class="{'no-spacing': noSpacing}"
>
<slot name="help">{{ helpText }}</slot>
</small>
Expand All @@ -28,13 +31,15 @@
/>
<div v-show="invalid"
class="invalid-feedback"
:class="{'no-spacing': noSpacing}"
>
<slot name="invalid">
{{ invalidText }}
</slot>
</div>
<div v-if="validText"
class="valid-feedback"
:class="{'no-spacing': noSpacing}"
:style="{display: valid&&!invalid? 'block':'none'}"
>
<slot name="valid">
Expand All @@ -61,6 +66,7 @@ interface Props {
valid?: boolean;
required?: boolean;
styleType?: FieldGroupStyleType;
noSpacing?: boolean;
}

export default defineComponent<Props>({
Expand Down Expand Up @@ -99,20 +105,24 @@ export default defineComponent<Props>({
type: String,
default: 'primary',
},
noSpacing: {
type: Boolean,
default: false,
},
},
});
</script>

<style lang="postcss">
.p-field-group {
&.primary {
&.primary:not(.no-spacing) {
margin-bottom: 1rem;
}
&.secondary {
&.secondary:not(.no-spacing) {
margin-bottom: 0.5rem;
}

.form-label {
.form-label:not(.no-spacing) {
padding-bottom: 0.25rem;
}
.label-box {
Expand All @@ -124,23 +134,32 @@ export default defineComponent<Props>({
font-size: 0.75rem;
line-height: 1.4;
margin-left: 0.25rem;
margin-bottom: 0.25rem;
font-weight: normal;
&:not(.no-spacing) {
margin-bottom: 0.25rem;
}
}
.help-msg {
@apply block mb-2;
@apply block;
&:not(.no-spacing) {
@apply mb-2;
}
}
.invalid-feedback {
@apply text-alert;
font-size: 0.75rem;
line-height: 0.875rem;
margin-top: 0.25rem;
&:not(.no-spacing) {
margin-top: 0.25rem;
}
}
.valid-feedback {
@apply text-safe;
font-size: 0.75rem;
line-height: 0.875rem;
margin-top: 0.25rem;
&:not(.no-spacing) {
margin-top: 0.25rem;
}
}
small {
font-size: 0.75rem;
Expand Down
16 changes: 16 additions & 0 deletions packages/mirinae/src/controls/forms/field-group/story-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const getPFieldGroupArgs = (): Args => ({
valid: false,
required: false,
styleType: 'primary',
noSpacing: false,
labelSlot: null,
labelExtraSlot: null,
validSlot: null,
Expand Down Expand Up @@ -147,6 +148,21 @@ export const getPFieldGroupArgTypes = (): ArgTypes => ({
control: 'select',
options: ['primary', 'secondary'],
},
noSpacing: {
name: 'noSpacing',
type: { name: 'boolean' },
description: 'Props for hide spacing',
table: {
type: {
summary: 'boolean',
},
category: 'props',
defaultValue: {
summary: false,
},
},
control: 'boolean',
},
labelSlot: {
name: 'label',
description: 'Slot for label',
Expand Down
Loading