diff --git a/packages/docs/page-config/ui-elements/modal/api-description.ts b/packages/docs/page-config/ui-elements/modal/api-description.ts index ccb890533b..11e6c36cf1 100644 --- a/packages/docs/page-config/ui-elements/modal/api-description.ts +++ b/packages/docs/page-config/ui-elements/modal/api-description.ts @@ -23,6 +23,8 @@ export default defineApiDescription({ overlayOpacity: "Set the overlay's opacity", anchorClass: "Set class name to the `anchor` slot container", zIndex: "Set the modal's `z-index`", + draggable: "Enable modal dragging", + draggablePosition: "Set the initial position of the draggable modal", allowBodyScroll: "Allows the document scroll while modal is open.", blur: "Use `blur` filter to overlay. Root `css` variable `--va-modal-overlay-background-blur-radius` sets the blur radius", ariaCloseLabel: "The aria-label of the close button", diff --git a/packages/docs/page-config/ui-elements/modal/examples/DraggableModals.vue b/packages/docs/page-config/ui-elements/modal/examples/DraggableModals.vue new file mode 100644 index 0000000000..ed6dfede4a --- /dev/null +++ b/packages/docs/page-config/ui-elements/modal/examples/DraggableModals.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/packages/docs/page-config/ui-elements/modal/examples/DraggableSlots.vue b/packages/docs/page-config/ui-elements/modal/examples/DraggableSlots.vue new file mode 100644 index 0000000000..8e6952e0b4 --- /dev/null +++ b/packages/docs/page-config/ui-elements/modal/examples/DraggableSlots.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/packages/docs/page-config/ui-elements/modal/index.ts b/packages/docs/page-config/ui-elements/modal/index.ts index 6caff80271..ede0c382ba 100644 --- a/packages/docs/page-config/ui-elements/modal/index.ts +++ b/packages/docs/page-config/ui-elements/modal/index.ts @@ -77,6 +77,16 @@ export default definePageConfig({ description: "Modals can be nested: you can open one modal from another." }), + block.example("DraggableModals", { + title: "Draggable modals", + description: "Modals can be draggable: you can open modal in 1 of 9 positions and drag it." + }), + + block.example("DraggableSlots", { + title: "Draggable slots", + description: "Draggable modal can be customize with any slots for drag interaction." + }), + block.subtitle("API"), block.api("VaModal", apiDescription, apiOptions), diff --git a/packages/docs/page-config/ui-elements/modal/modal-api.ts b/packages/docs/page-config/ui-elements/modal/modal-api.ts index bfef579477..c6aac26765 100644 --- a/packages/docs/page-config/ui-elements/modal/modal-api.ts +++ b/packages/docs/page-config/ui-elements/modal/modal-api.ts @@ -38,6 +38,8 @@ const modalOptions: Record = { overlay: "boolean", overlayOpacity: "number | string", zIndex: "number | string", + draggable: "boolean", + draggablePosition: "{ x?: number | string, y?: number | string }", onOk: "() => void", onCancel: "() => void", onClickOutside: "() => void", diff --git a/packages/ui/src/components/va-modal/VaModal.stories.ts b/packages/ui/src/components/va-modal/VaModal.stories.ts index 579cda27cc..4e36b0ae16 100644 --- a/packages/ui/src/components/va-modal/VaModal.stories.ts +++ b/packages/ui/src/components/va-modal/VaModal.stories.ts @@ -5,9 +5,12 @@ import { StoryFn } from '@storybook/vue3' import { expect } from '@storybook/jest' import { userEvent } from '../../../.storybook/interaction-utils/userEvent' +import VaIcon from '../va-icon/VaIcon.vue' + export default { title: 'VaModal', component: VaModal, + tags: ['autodocs'], } export const OldDemos: StoryFn = () => ({ @@ -98,3 +101,201 @@ export const childProps: StoryFn = () => ({ `, }) + +// TODO: add tests +type PositionType = number | `${number}%` +type initialPositionType = { x?: PositionType; y?: PositionType } +export const DraggableAndAttachElement: StoryFn = () => ({ + components: { VaModal, VaIcon }, + setup () { + const iconsList: { + title: string; + name: string; + flip?: string; + position: initialPositionType; + }[] = [ + { title: 'Top Left', name: 'arrow_outward', flip: 'vertical', position: { x: 0, y: 0 } }, + { title: 'Top Center', name: 'arrow_upward', position: { x: '50%', y: 0 } }, + { title: 'Top Right', name: 'arrow_outward', position: { x: '100%', y: 0 } }, + { title: 'Center Left', name: 'arrow_back', position: { x: 0, y: '50%' } }, + { title: 'Center Center', name: 'circle', position: { x: '50%', y: '50%' } }, + { title: 'Center Right', name: 'arrow_forward', position: { x: '100%', y: '50%' } }, + { title: 'Bottom Left', name: 'arrow_outward', flip: 'both', position: { x: 0, y: '100%' } }, + { title: 'Bottom Center', name: 'arrow_downward', position: { x: '50%', y: '100%' } }, + { title: 'Bottom Right', name: 'arrow_outward', flip: 'horizontal', position: { x: '100%', y: '100%' } }, + ] + + const showModal = ref(false) + const title = ref(null) + const initialPosition = ref(null) + + const handleIconClick = (currentTitle: string, position: initialPositionType) => { + title.value = currentTitle + initialPosition.value = position + showModal.value = true + } + + const iconsStyle = { + display: 'flex', + flexWrap: 'wrap', + width: '80px', + gap: '4px', + alignItems: 'Center', + } + + const attachElementStyle = { + width: '300px', + height: '300px', + border: '2px solid black', + } + + return { iconsList, showModal, title, initialPosition, handleIconClick, iconsStyle, attachElementStyle } + }, + template: ` +
+ +
+