@v-c/notification 2.0.0-rc.1 → 2.0.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,9 +5,32 @@ import useStack from "../hooks/useStack.js";
5
5
  import Content from "./Content.js";
6
6
  import { TransitionGroup, computed, createVNode, defineComponent, isVNode, mergeProps, ref, shallowRef, toRef, watch, watchEffect } from "vue";
7
7
  import { clsx } from "@v-c/util";
8
- import { getTransitionGroupProps } from "@v-c/util/dist/utils/transition";
9
8
  import { unrefElement } from "@v-c/util/dist/vueuse/unref-element";
10
9
  //#region src/NotificationList/index.tsx
10
+ /**
11
+ * Map Vue's TransitionGroup enter/leave class hooks onto the rc-motion
12
+ * style class names that antdv-next 6.4.0 notification styles target
13
+ * (-enter-start / -enter-active / -leave-start / -leave-active).
14
+ *
15
+ * The shared @v-c/util getTransitionGroupProps puts -leave-active in the
16
+ * leaveActiveClass, which means the notice jumps straight to opacity:0
17
+ * on leave without animating. Wire each phase to the correct rc-motion
18
+ * suffix so the opacity/transform transition runs.
19
+ */
20
+ function buildMotionGroupProps(name, override) {
21
+ return {
22
+ name,
23
+ appear: true,
24
+ enterFromClass: `${name} ${name}-enter ${name}-appear ${name}-enter-start ${name}-appear-start`,
25
+ enterActiveClass: `${name} ${name}-enter ${name}-appear`,
26
+ enterToClass: `${name} ${name}-enter ${name}-appear ${name}-enter-active ${name}-appear-active`,
27
+ leaveFromClass: `${name} ${name}-leave ${name}-leave-start`,
28
+ leaveActiveClass: `${name} ${name}-leave`,
29
+ leaveToClass: `${name} ${name}-leave ${name}-leave-active`,
30
+ moveClass: `${name} ${name}-move`,
31
+ ...override
32
+ };
33
+ }
11
34
  function _isSlot(s) {
12
35
  return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s);
13
36
  }
@@ -90,9 +113,9 @@ var NotificationList = /* @__PURE__ */ defineComponent((props, { attrs }) => {
90
113
  const listPrefixCls = `${prefixCls}-list`;
91
114
  const positionResult = position.value;
92
115
  let motionGroupProps = {};
93
- if (placementMotion.value) motionGroupProps = getTransitionGroupProps(placementMotion.value.name, placementMotion.value);
116
+ if (placementMotion.value?.name) motionGroupProps = buildMotionGroupProps(placementMotion.value.name, placementMotion.value);
94
117
  const renderItems = () => keys.value.map((config) => {
95
- const { key, placement: _itemPlacement, classNames: configClassNames, styles: configStyles, className: configClassName, style: configStyle, ...notificationConfig } = config;
118
+ const { key, placement: _itemPlacement, classNames: configClassNames, styles: configStyles, className: configClassName, style: configStyle, onClose: configOnClose, ...notificationConfig } = config;
96
119
  const strKey = String(key);
97
120
  const notificationIndex = getIndex(keys.value, key);
98
121
  const stackInThreshold = stackEnabled.value && notificationIndex !== void 0 && notificationIndex < (stackParams.threshold?.value ?? 0);
@@ -115,7 +138,7 @@ var NotificationList = /* @__PURE__ */ defineComponent((props, { attrs }) => {
115
138
  "notificationIndex": notificationIndex,
116
139
  "stackInThreshold": stackInThreshold,
117
140
  "onClose": () => {
118
- config.onClose?.();
141
+ configOnClose?.();
119
142
  onNoticeClose?.(key);
120
143
  }
121
144
  }), null);
@@ -145,10 +168,7 @@ var NotificationList = /* @__PURE__ */ defineComponent((props, { attrs }) => {
145
168
  "topNoticeWidth": positionResult.topNoticeWidth,
146
169
  "className": ncs?.listContent,
147
170
  "style": nss?.listContent
148
- }, { default: () => [createVNode(TransitionGroup, mergeProps({
149
- "tag": false,
150
- "appear": true
151
- }, motionGroupProps, { "onAfterLeave": checkAllClosed }), _isSlot(_slot = renderItems()) ? _slot : { default: () => [_slot] })] })]);
171
+ }, { default: () => [createVNode(TransitionGroup, mergeProps({ "appear": true }, motionGroupProps, { "onAfterLeave": checkAllClosed }), _isSlot(_slot = renderItems()) ? _slot : { default: () => [_slot] })] })]);
152
172
  };
153
173
  }, {
154
174
  props: {
@@ -8,10 +8,6 @@ var defaults = { prefixCls: "vc-notification" };
8
8
  var Notifications = /* @__PURE__ */ defineComponent((props, { expose }) => {
9
9
  const configList = shallowRef([]);
10
10
  const onNoticeClose = (key) => {
11
- const config = configList.value.find((item) => item.key === key);
12
- const closable = config?.closable;
13
- (closable && typeof closable === "object" ? closable : null)?.onClose?.();
14
- config?.onClose?.();
15
11
  configList.value = configList.value.filter((item) => item.key !== key);
16
12
  };
17
13
  expose({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@v-c/notification",
3
3
  "type": "module",
4
- "version": "2.0.0-rc.1",
4
+ "version": "2.0.0-rc.3",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -7,9 +7,37 @@ import type {
7
7
  NotificationStyles as NoticeStyles,
8
8
  } from '../Notification'
9
9
  import { clsx } from '@v-c/util'
10
- import { getTransitionGroupProps } from '@v-c/util/dist/utils/transition'
11
10
  import { unrefElement } from '@v-c/util/dist/vueuse/unref-element'
12
11
  import { computed, defineComponent, ref, shallowRef, toRef, TransitionGroup, watch, watchEffect } from 'vue'
12
+
13
+ /**
14
+ * Map Vue's TransitionGroup enter/leave class hooks onto the rc-motion
15
+ * style class names that antdv-next 6.4.0 notification styles target
16
+ * (-enter-start / -enter-active / -leave-start / -leave-active).
17
+ *
18
+ * The shared @v-c/util getTransitionGroupProps puts -leave-active in the
19
+ * leaveActiveClass, which means the notice jumps straight to opacity:0
20
+ * on leave without animating. Wire each phase to the correct rc-motion
21
+ * suffix so the opacity/transform transition runs.
22
+ */
23
+ function buildMotionGroupProps(name: string, override?: Partial<TransitionGroupProps>): TransitionGroupProps {
24
+ return {
25
+ name,
26
+ appear: true,
27
+ // ENTER: from = opacity 0 (-enter-start / -appear-start)
28
+ // to = opacity 1 (-enter-active / -appear-active)
29
+ enterFromClass: `${name} ${name}-enter ${name}-appear ${name}-enter-start ${name}-appear-start`,
30
+ enterActiveClass: `${name} ${name}-enter ${name}-appear`,
31
+ enterToClass: `${name} ${name}-enter ${name}-appear ${name}-enter-active ${name}-appear-active`,
32
+ // LEAVE: from = opacity 1 (-leave-start)
33
+ // to = opacity 0 (-leave-active)
34
+ leaveFromClass: `${name} ${name}-leave ${name}-leave-start`,
35
+ leaveActiveClass: `${name} ${name}-leave`,
36
+ leaveToClass: `${name} ${name}-leave ${name}-leave-active`,
37
+ moveClass: `${name} ${name}-move`,
38
+ ...override,
39
+ }
40
+ }
13
41
  import useListPosition from '../hooks/useListPosition'
14
42
  import useStack from '../hooks/useStack'
15
43
  import Notification from '../Notification'
@@ -185,13 +213,26 @@ const NotificationList = defineComponent<NotificationListProps>(
185
213
  const positionResult = position.value
186
214
 
187
215
  let motionGroupProps: TransitionGroupProps = {}
188
- if (placementMotion.value) {
189
- motionGroupProps = getTransitionGroupProps(placementMotion.value.name!, placementMotion.value)
216
+ if (placementMotion.value?.name) {
217
+ motionGroupProps = buildMotionGroupProps(placementMotion.value.name, placementMotion.value)
190
218
  }
191
219
 
192
220
  const renderItems = () =>
193
221
  keys.value.map((config) => {
194
- const { key, placement: _itemPlacement, classNames: configClassNames, styles: configStyles, className: configClassName, style: configStyle, ...notificationConfig } = config
222
+ const {
223
+ key,
224
+ placement: _itemPlacement,
225
+ classNames: configClassNames,
226
+ styles: configStyles,
227
+ className: configClassName,
228
+ style: configStyle,
229
+ // Extract onClose so the spread below does not also bind it.
230
+ // Vue would otherwise merge our `onClose={...}` and the spread's
231
+ // `onClose` into an Array, breaking `props.onClose?.()` in the
232
+ // notice itself.
233
+ onClose: configOnClose,
234
+ ...notificationConfig
235
+ } = config
195
236
  const strKey = String(key)
196
237
  const notificationIndex = getIndex(keys.value, key)
197
238
  const stackInThreshold
@@ -220,7 +261,7 @@ const NotificationList = defineComponent<NotificationListProps>(
220
261
  notificationIndex={notificationIndex}
221
262
  stackInThreshold={stackInThreshold}
222
263
  onClose={() => {
223
- (config as NotificationListConfig).onClose?.()
264
+ configOnClose?.()
224
265
  onNoticeClose?.(key)
225
266
  }}
226
267
  />
@@ -261,7 +302,6 @@ const NotificationList = defineComponent<NotificationListProps>(
261
302
  style={nss?.listContent}
262
303
  >
263
304
  <TransitionGroup
264
- tag={false as any}
265
305
  appear
266
306
  {...motionGroupProps}
267
307
  onAfterLeave={checkAllClosed}
@@ -42,11 +42,6 @@ const Notifications = defineComponent<NotificationsProps>(
42
42
  const configList = shallowRef<NotificationListConfig[]>([])
43
43
 
44
44
  const onNoticeClose = (key: Key) => {
45
- const config = configList.value.find(item => item.key === key)
46
- const closable = config?.closable
47
- const closableObj = closable && typeof closable === 'object' ? closable : null
48
- closableObj?.onClose?.()
49
- config?.onClose?.()
50
45
  configList.value = configList.value.filter(item => item.key !== key)
51
46
  }
52
47
 
@@ -0,0 +1,70 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { nextTick } from 'vue'
3
+ import { afterEach, describe, expect, it, vi } from 'vitest'
4
+ import Notifications from '../src/Notifications'
5
+
6
+ describe('notification', () => {
7
+ afterEach(() => {
8
+ document.body.innerHTML = ''
9
+ })
10
+
11
+ it('calls close callbacks once when notice close button is clicked', async () => {
12
+ const onClose = vi.fn()
13
+ const closableOnClose = vi.fn()
14
+ const wrapper = mount(Notifications, {
15
+ props: {
16
+ container: document.body,
17
+ },
18
+ attachTo: document.body,
19
+ })
20
+
21
+ wrapper.vm.open({
22
+ key: 'notice',
23
+ title: 'Notice',
24
+ duration: false,
25
+ closable: {
26
+ closeIcon: 'x',
27
+ onClose: closableOnClose,
28
+ },
29
+ onClose,
30
+ })
31
+
32
+ await nextTick()
33
+ await nextTick()
34
+ await document.querySelector<HTMLButtonElement>('.vc-notification-notice-close')!.click()
35
+ await nextTick()
36
+
37
+ expect(closableOnClose).toHaveBeenCalledTimes(1)
38
+ expect(onClose).toHaveBeenCalledTimes(1)
39
+ })
40
+
41
+ it('does not call notice close callbacks when closed by api', async () => {
42
+ const onClose = vi.fn()
43
+ const closableOnClose = vi.fn()
44
+ const wrapper = mount(Notifications, {
45
+ props: {
46
+ container: document.body,
47
+ },
48
+ attachTo: document.body,
49
+ })
50
+
51
+ wrapper.vm.open({
52
+ key: 'notice',
53
+ title: 'Notice',
54
+ duration: false,
55
+ closable: {
56
+ closeIcon: 'x',
57
+ onClose: closableOnClose,
58
+ },
59
+ onClose,
60
+ })
61
+
62
+ await nextTick()
63
+ await nextTick()
64
+ wrapper.vm.close('notice')
65
+ await nextTick()
66
+
67
+ expect(closableOnClose).not.toHaveBeenCalled()
68
+ expect(onClose).not.toHaveBeenCalled()
69
+ })
70
+ })
package/vitest.config.ts CHANGED
@@ -4,6 +4,8 @@ import configShared from '../../vitest.config'
4
4
  export default mergeConfig(
5
5
  configShared,
6
6
  defineProject({
7
-
7
+ test: {
8
+ environment: 'jsdom',
9
+ },
8
10
  }),
9
11
  )