From 27d49f9844640cc9048a2b1c2dcb40f97af94b09 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 17 Jun 2025 11:36:39 +0100 Subject: [PATCH 1/5] Export modal components --- resources/js/components/ui/index.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/resources/js/components/ui/index.js b/resources/js/components/ui/index.js index b5721872d84..2981d0437dc 100644 --- a/resources/js/components/ui/index.js +++ b/resources/js/components/ui/index.js @@ -35,6 +35,9 @@ import { default as Heading } from './Heading.vue'; import { default as Icon } from './Icon.vue'; import { default as Input } from './Input/Input.vue'; import { default as Label } from './Label.vue'; +import { default as Modal } from './Modal/Modal.vue'; +import { default as ModalClose } from './Modal/Close.vue'; +import { default as ModalTitle } from './Modal/Title.vue'; import { default as Panel } from './Panel/Panel.vue'; import { default as PanelFooter } from './Panel/Footer.vue'; import { default as PanelHeader } from './Panel/Header.vue'; @@ -107,6 +110,9 @@ export { Icon, Input, Label, + Modal, + ModalClose, + ModalTitle, Panel, PanelFooter, PanelHeader, From a9cc6a4531214a6a667e1d606ea832a9d52ad45a Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 17 Jun 2025 11:39:53 +0100 Subject: [PATCH 2/5] Allow modal open state to be controlled by parent components Otherwise, as soon as the `` component is rendered, its displayed. The modal can still be used *without* the parent component managing state. --- resources/js/components/ui/Modal/Modal.vue | 28 +++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/resources/js/components/ui/Modal/Modal.vue b/resources/js/components/ui/Modal/Modal.vue index 074f3824590..ad845bd98f9 100644 --- a/resources/js/components/ui/Modal/Modal.vue +++ b/resources/js/components/ui/Modal/Modal.vue @@ -2,9 +2,13 @@ import { cva } from 'cva'; import { hasComponent } from '@statamic/composables/has-component.js'; import { DialogContent, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger } from 'reka-ui'; +import { getCurrentInstance, ref, watch } from 'vue'; + +const emit = defineEmits(['update:open']); const props = defineProps({ title: { type: String, default: '' }, + open: { type: Boolean, default: false }, }); const hasModalTitleComponent = hasComponent('ModalTitle'); @@ -22,10 +26,32 @@ const modalClasses = cva({ 'slide-in-from-top-2', ], })({}); + +const instance = getCurrentInstance(); +const isUsingOpenProp = instance && 'open' in instance.vnode.props; + +const open = ref(props.open); + +watch( + () => props.open, + (value) => open.value = value, +); + +// When the parent component controls the open state, emit an update event +// so it can update its state, which eventually gets passed down as a prop. +// Otherwise, just update the local state. +function updateOpen(value) { + if (isUsingOpenProp) { + emit('update:open', value); + return; + } + + open.value = value; +} + + - - diff --git a/resources/js/components/collections/DeleteLocalizationConfirmation.vue b/resources/js/components/collections/DeleteLocalizationConfirmation.vue index 8c81c2a4b82..2e8ce9da0dd 100644 --- a/resources/js/components/collections/DeleteLocalizationConfirmation.vue +++ b/resources/js/components/collections/DeleteLocalizationConfirmation.vue @@ -1,63 +1,57 @@ + + diff --git a/resources/js/components/modals/ElevatedSessionModal.vue b/resources/js/components/modals/ElevatedSessionModal.vue index 7012fec1174..121de9694ca 100644 --- a/resources/js/components/modals/ElevatedSessionModal.vue +++ b/resources/js/components/modals/ElevatedSessionModal.vue @@ -1,73 +1,73 @@ - + + - - diff --git a/resources/js/components/two-factor/RecoveryCodesModal.vue b/resources/js/components/two-factor/RecoveryCodesModal.vue index 283dc6289ea..8d955c4bbd6 100644 --- a/resources/js/components/two-factor/RecoveryCodesModal.vue +++ b/resources/js/components/two-factor/RecoveryCodesModal.vue @@ -2,6 +2,7 @@ import { ref, onMounted } from 'vue'; import LoadingGraphic from '@statamic/components/LoadingGraphic.vue'; import axios from 'axios'; +import { Modal, Button } from '@statamic/ui'; const emit = defineEmits(['cancel', 'close']); @@ -46,20 +47,13 @@ function copyToClipboard() {