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: migrate BaseEventsModal to composition API #1499

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/_vue3-migration-test/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export { default as TestHighlight } from './test-highlight.vue';
export { default as TestBaseResultImages } from './result/test-base-result-images.vue';
export { default as TestBasePanel } from './panels/test-base-panel.vue';
export { default as TestBaseKeyboardNavigation } from './test-base-keyboard-navigation.vue';
export { default as TestBaseEventsModal } from './modals/test-base-events-modal.vue';
export { default as TestBaseIdModal } from './modals/test-base-id-modal.vue';
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<template>
<div class="base-modal">
<button @click="openModal">Open modal</button>
<BaseEventsModal
:animation="Fade"
:focusOnOpen="true"
v-bind="$attrs"
contentClass="content"
overlayClass="overlay"
>
<h1>Hello</h1>
<p>The modal is working with events!!</p>
<button @click="closeModal">Close modal</button>
</BaseEventsModal>
</div>
</template>

<script setup lang="ts">
import BaseEventsModal from '../../../../x-components/src/components/modals/base-events-modal.vue';
import Fade from '../../../../x-components/src/components/animations/fade.vue';
import { use$x } from '../../../../x-components/src/composables/use-$x';
const _$x = use$x();

const openModal = () => _$x.emit('UserClickedOpenEventsModal');

const closeModal = () => _$x.emit('UserClickedCloseEventsModal');
</script>

<style>
.base-modal {
.content {
background: white;
margin: auto;
width: 50%;
border: 3px solid green;
padding: 10px;
}

.overlay {
background-color: #00ff80;
}
}
</style>
6 changes: 6 additions & 0 deletions packages/_vue3-migration-test/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
TestBaseResultImages,
TestBasePanel,
TestBaseKeyboardNavigation,
TestBaseEventsModal,
TestBaseIdModal
} from './';

Expand Down Expand Up @@ -166,6 +167,11 @@ const routes = [
name: 'TestBaseKeyboardNavigation',
component: TestBaseKeyboardNavigation
},
{
path: '/base-events-modal',
name: 'BaseEventsModal',
component: TestBaseEventsModal
},
{
path: '/test-base-id-modal',
name: 'TestBaseIdModal',
Expand Down
191 changes: 106 additions & 85 deletions packages/x-components/src/components/modals/base-events-modal.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
<template>
<BaseModal
v-bind="$attrs"
ref="baseModalEl"
victorcg88 marked this conversation as resolved.
Show resolved Hide resolved
@click:overlay="emitBodyClickEvent"
@focusin:body="emitBodyClickEvent"
:animation="animation"
:open="isOpen"
v-bind="$attrs"
>
<slot />
</BaseModal>
</template>

<script lang="ts">
import Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
import { defineComponent, PropType, ref } from 'vue';
import { XEvent } from '../../wiring/events.types';
import { XOn } from '../decorators/bus.decorators';
import { WireMetadata } from '../../wiring/wiring.types';
import { getTargetElement, isElementEqualOrContained } from '../../utils/html';
import { AnimationProp } from '../../types/animation-prop';
import { use$x } from '../../composables/use-$x';
import BaseModal from './base-modal.vue';

/**
Expand All @@ -30,88 +30,109 @@
*
* @public
*/
@Component({
components: { BaseModal }
})
export default class BaseEventsModal extends Vue {
/**
* Animation to use for opening/closing the modal.
*/
@Prop()
public animation?: Vue | string;
/**
* Array of {@link XEvent} to listen to open the modal.
*/
@Prop({ default: (): XEvent[] => ['UserClickedOpenEventsModal'] })
public eventsToOpenModal!: XEvent[];

/**
* Array of {@link XEvent} to listen to close the modal.
*/
@Prop({
default: (): XEvent[] => ['UserClickedCloseEventsModal', 'UserClickedOutOfEventsModal']
})
public eventsToCloseModal!: XEvent[];

/**
* Event to emit when any part of the website out of the modal has been clicked.
*/
@Prop({ default: 'UserClickedOutOfEventsModal' })
public bodyClickEvent!: XEvent;

/**
* Whether the modal is open or not.
*/
protected isOpen = false;

/** The element that opened the modal. */
protected openerElement?: HTMLElement;

/**
* Opens the modal.
*
* @param _payload - The payload of the event that opened the modal.
* @param metadata - The metadata of the event that opened the modal.
*
* @internal
*/
@XOn(component => (component as BaseEventsModal).eventsToOpenModal)
openModal(_payload: unknown, metadata: WireMetadata): void {
if (!this.isOpen) {
this.openerElement = metadata.target;
this.isOpen = true;
}
}

/**
* Closes the modal.
*
* @internal
*/
@XOn(component => (component as BaseEventsModal).eventsToCloseModal)
closeModal(): void {
if (this.isOpen) {
this.isOpen = false;
}
}

/**
* Emits the event defined in the {@link BaseEventsModal.bodyClickEvent} event unless the passed
* event target is the button that opened the modal.
*
* @param event - The event that triggered the close attempt.
* @public
*/
protected emitBodyClickEvent(event: MouseEvent | FocusEvent): void {
// Prevents clicking the open button when the panel is already open to close the panel.
if (
!this.openerElement ||
!isElementEqualOrContained(this.openerElement, getTargetElement(event))
) {
this.$x.emit(this.bodyClickEvent, undefined, { target: this.$el as HTMLElement });
export default defineComponent({
name: 'BaseEventsModal',
components: {
BaseModal
},
props: {
/**
* Animation to use for opening/closing the modal.
*/
animation: AnimationProp,

/**
* Array of {@link XEvent} to listen to open the modal.
*/
eventsToOpenModal: {
type: Array as PropType<XEvent[]>,
default: (): XEvent[] => ['UserClickedOpenEventsModal']
},

/**
* Array of {@link XEvent} to listen to close the modal.
*/
eventsToCloseModal: {
type: Array as PropType<XEvent[]>,
default: (): XEvent[] => ['UserClickedCloseEventsModal', 'UserClickedOutOfEventsModal']
},

/**
* Event to emit when any part of the website out of the modal has been clicked.
*/
bodyClickEvent: {
type: String as PropType<XEvent>,
default: 'UserClickedOutOfEventsModal'
}
},
setup(props) {
const $x = use$x();

/**
* Whether the modal is open or not.
*/
const isOpen = ref(false);

/** The element that opened the modal. */
const openerElement = ref<HTMLElement>();

const baseModalEl = ref<HTMLElement>();

/**
* Opens the modal.
*
* @param _payload - The payload of the event that opened the modal.
* @param metadata - The metadata of the event that opened the modal.
*
* @internal
*/
props.eventsToOpenModal?.forEach(event =>
$x.on(event, true).subscribe(({ metadata }) => {
if (!isOpen.value) {
openerElement.value = metadata.target;
isOpen.value = true;
}
})
);

/**
* Closes the modal.
*
* @internal
*/
props.eventsToCloseModal?.forEach(event =>
$x.on(event, false).subscribe(() => {
if (isOpen.value) {
isOpen.value = false;
}
})
);

/**
* Emits the event defined in the {@link BaseEventsModal.bodyClickEvent} event unless the passed
* event target is the button that opened the modal.
*
* @param event - The event that triggered the close attempt.
* @public
*/
const emitBodyClickEvent = (event: MouseEvent | FocusEvent) => {
// Prevents clicking the open button when the panel is already open to close the panel.
if (
!openerElement.value ||
!isElementEqualOrContained(openerElement.value, getTargetElement(event))
) {
$x.emit(props.bodyClickEvent, undefined, { target: baseModalEl.value as HTMLElement });
}
};

return {
isOpen,
openerElement,
baseModalEl,
emitBodyClickEvent
};
}
}
});
</script>

<docs lang="mdx">
Expand Down
Loading