From db48216ab3c707f3688cbf8237d16fd67772946d Mon Sep 17 00:00:00 2001 From: daief <1437931235@qq.com> Date: Tue, 12 Apr 2022 22:12:46 +0800 Subject: [PATCH] feat: init modal base --- README.md | 7 +- docs/src/pages/components/popper.md | 22 ++--- docs/src/pages/demo.md | 43 ++++++--- package.json | 2 +- src/components/index.tsx | 1 + src/components/modal/base.tsx | 130 +++++++++++++++++++++++++++ src/components/modal/index.tsx | 1 + src/components/modal/style/base.less | 28 ++++++ 8 files changed, 208 insertions(+), 26 deletions(-) create mode 100644 src/components/modal/base.tsx create mode 100644 src/components/modal/index.tsx create mode 100644 src/components/modal/style/base.less diff --git a/README.md b/README.md index 5df5a93..2ab320f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # daisyui-vue -基于 daisyui 封装的 vue 组件库。 +基于 [daisyui](https://github.com/saadeghi/daisyui) 封装的 vue 组件库。 ## Usage @@ -140,3 +140,8 @@ export function render() { - [ ] mockup-code - [ ] mockup-phone - [ ] mockup-window + +## 命名规则 + +- 事件名称,`onNameAction`:onMaskClick、onEscKeyDown +- 事件控制,`xxxYYable`: keyboardCloseable diff --git a/docs/src/pages/components/popper.md b/docs/src/pages/components/popper.md index d6c90fe..8aef23d 100644 --- a/docs/src/pages/components/popper.md +++ b/docs/src/pages/components/popper.md @@ -225,17 +225,17 @@ template slots ### Attributes -| name | description | type | default | -| ------------------ | ------------------------------------ | -------------------------------- | ------- | -| content | popper content | any, function | - | -| open | popper open status | boolean | - | -| placement | popper placement | Placement | auto | -| disableTeleport | disable popper node teleport to body | boolean | false | -| disabled | disable popper | boolean | true | -| onChange | emitted whe popper status change | function | - | -| triggerAction | the action to tigger popper | contextMenu, hover, click, focus | hover | -| outsideCloseable | click outside to close popper | boolean | true | -| escapeKeyCloseable | press escape to close popper | boolean | true | +| name | description | type | default | +| ---------------- | ------------------------------------ | -------------------------------- | ------- | +| content | popper content | any, function | - | +| open | popper open status | boolean | - | +| placement | popper placement | Placement | auto | +| disableTeleport | disable popper node teleport to body | boolean | false | +| disabled | disable popper | boolean | true | +| onChange | emitted whe popper status change | function | - | +| triggerAction | the action to tigger popper | contextMenu, hover, click, focus | hover | +| outsideCloseable | click outside to close popper | boolean | true | +| escapeCloseable | press escape key to close popper | boolean | true | ### Slots diff --git a/docs/src/pages/demo.md b/docs/src/pages/demo.md index 1c0d4dc..4d64ba0 100644 --- a/docs/src/pages/demo.md +++ b/docs/src/pages/demo.md @@ -1,18 +1,35 @@ # Demo for development -```html :::demo -
-
+```tsx :::run +import { reactive, watch } from 'vue'; - - click - +export default { + setup: () => { + const state = reactive({ + show: false, + }); -
-
+ let c = 0; + + watch([() => c], (newVal, oldVal) => { + console.log(newVal, oldVal); + }); + + return () => ( +
+ (state.show = e.target.checked)} + /> +
+ (state.show = false)} + custom={() =>
c++}>111 - {c}
} + >
+
+ ); + }, +}; ``` diff --git a/package.json b/package.json index 73c325e..9b926e5 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "babel-plugin-macros": "^3.1.0", "babel-plugin-twin": "^1.0.2", "cssnano": "^5.0.10", - "daisyui": "2.13.4", + "daisyui": "2.13.6", "download-git-repo": "^3.0.2", "fs-extra": "^10.0.0", "glob": "^7.1.7", diff --git a/src/components/index.tsx b/src/components/index.tsx index 8b647f5..fbd2e5a 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -14,6 +14,7 @@ export * from './input'; export * from './link'; export * from './mask'; export * from './menu'; +export * from './modal'; export * from './navbar'; export * from './popper'; export * from './progress'; diff --git a/src/components/modal/base.tsx b/src/components/modal/base.tsx new file mode 100644 index 0000000..ab6f446 --- /dev/null +++ b/src/components/modal/base.tsx @@ -0,0 +1,130 @@ +import { useEventListener } from '@/shared/hooks/useEventListener'; +import { componentV2 } from '@/shared/styled'; +import { ExtractFromProps } from '@/shared/types/common'; +import { getRenderResult } from '@/shared/utils'; +import { + computed, + PropType, + reactive, + StyleValue, + Teleport, + vShow, + watch, + withDirectives, +} from 'vue'; +import style from './style/base.less'; + +export const modalBaseProps = { + disableTeleport: { + type: Boolean, + default: false, + }, + container: { + type: [Function, String, Object] as PropType< + string | Element | (() => Element) + >, + default: 'body', + }, + open: Boolean, + hideMask: Boolean, + maskCloseable: { + type: Boolean, + default: true, + }, + onMaskClick: Function as PropType<(e: MouseEvent) => void>, + escapeCloseable: { + type: Boolean, + default: true, + }, + onEscKeyDown: Function as PropType<(e: KeyboardEvent) => void>, + onClose: Function as PropType< + (e: MouseEvent | KeyboardEvent, trigger: 'esc' | 'mask') => void + >, + + custom: { default: '' }, + // TODO + destroyOnClose: Boolean, +}; + +export type IModalBaseProps = ExtractFromProps; + +export const ModalBase = componentV2( + { + name: 'ModalBase', + props: modalBaseProps, + inheritAttrs: false, + setup: (props, { slots, attrs }) => { + const state = reactive({ + hasTriggered: !!props.open, + }); + + const maskStyle = computed(() => [ + props.hideMask + ? { + pointerEvents: 'none', + background: 'none', + } + : '', + !props.maskCloseable ? { cursor: 'auto' } : '', + ]); + + const stop = watch( + () => props.open, + (newVal) => { + if (newVal) { + state.hasTriggered = true; + stop(); + } + }, + ); + + const handleClickMask = (e: MouseEvent) => { + if (props.maskCloseable && e.target === e.currentTarget) { + props.onMaskClick?.(e); + props.onClose?.(e, 'mask'); + } + }; + + useEventListener( + () => document, + 'keydown', + (e) => { + if (props.escapeCloseable && e.key === 'Escape') { + props.onEscKeyDown?.(e); + props.onClose?.(e, 'esc'); + } + }, + ); + + return () => { + const toContainer = + typeof props.container === 'function' + ? props.container() + : props.container; + + const customNode = getRenderResult('custom', props, slots); + + return state.hasTriggered ? ( + + {withDirectives( + , + [[vShow, props.open]], + )} + + ) : null; + }; + }, + }, + [style], +); diff --git a/src/components/modal/index.tsx b/src/components/modal/index.tsx new file mode 100644 index 0000000..8a185aa --- /dev/null +++ b/src/components/modal/index.tsx @@ -0,0 +1 @@ +export * from './base'; diff --git a/src/components/modal/style/base.less b/src/components/modal/style/base.less new file mode 100644 index 0000000..8c45911 --- /dev/null +++ b/src/components/modal/style/base.less @@ -0,0 +1,28 @@ +:root { + --modal-base-z-index: 1000; +} + +.dv-modal-base { + @apply cursor-pointer opacity-100; + @apply bg-neutral-focus bg-opacity-40 duration-200 ease-in-out; + @apply fixed inset-0 outline-0; + transition-property: transform, opacity; + overflow: hidden auto; + overscroll-behavior: contain; + z-index: var(--modal-base-z-index); +} + +.dv-modal-box { + @apply max-w-full translate-y-10 transform bg-base-100 p-6 transition duration-200 ease-in-out rounded-box; + @apply cursor-auto; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + pointer-events: auto; + width: 34rem; + max-height: calc(100vh - 5em); + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + overflow-y: auto; + overscroll-behavior: contain; +}