@v-c/notification 0.0.2

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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/bump.config.ts +6 -0
  3. package/dist/Notice.cjs +230 -0
  4. package/dist/Notice.d.ts +15 -0
  5. package/dist/Notice.js +225 -0
  6. package/dist/NoticeList.cjs +157 -0
  7. package/dist/NoticeList.d.ts +13 -0
  8. package/dist/NoticeList.js +154 -0
  9. package/dist/NotificationProvider.cjs +13 -0
  10. package/dist/NotificationProvider.d.ts +10 -0
  11. package/dist/NotificationProvider.js +10 -0
  12. package/dist/Notifications.cjs +146 -0
  13. package/dist/Notifications.d.ts +24 -0
  14. package/dist/Notifications.js +143 -0
  15. package/dist/_virtual/rolldown_runtime.cjs +21 -0
  16. package/dist/hooks/useNotification.cjs +80 -0
  17. package/dist/hooks/useNotification.d.ts +36 -0
  18. package/dist/hooks/useNotification.js +78 -0
  19. package/dist/hooks/useStack.cjs +24 -0
  20. package/dist/hooks/useStack.d.ts +6 -0
  21. package/dist/hooks/useStack.js +22 -0
  22. package/dist/index.cjs +6 -0
  23. package/dist/index.d.ts +6 -0
  24. package/dist/index.js +4 -0
  25. package/dist/interface.cjs +0 -0
  26. package/dist/interface.d.ts +55 -0
  27. package/dist/interface.js +0 -0
  28. package/docs/context.vue +34 -0
  29. package/docs/hooks.vue +89 -0
  30. package/docs/index.less +265 -0
  31. package/docs/maxCount.vue +24 -0
  32. package/docs/motion.ts +33 -0
  33. package/docs/notification.stories.vue +31 -0
  34. package/docs/showProgress.vue +34 -0
  35. package/docs/stack.vue +39 -0
  36. package/package.json +30 -0
  37. package/src/Notice.tsx +212 -0
  38. package/src/NoticeList.tsx +203 -0
  39. package/src/NotificationProvider.tsx +19 -0
  40. package/src/Notifications.tsx +164 -0
  41. package/src/hooks/useNotification.tsx +163 -0
  42. package/src/hooks/useStack.ts +32 -0
  43. package/src/index.ts +9 -0
  44. package/src/interface.ts +61 -0
  45. package/tsconfig.json +7 -0
  46. package/vite.config.ts +18 -0
  47. package/vitest.config.ts +9 -0
@@ -0,0 +1,78 @@
1
+ import Notifications_default from "../Notifications.js";
2
+ import { createVNode, onMounted, shallowRef, watch } from "vue";
3
+ var defaultGetContainer = () => document.body;
4
+ var uniqueKey = 0;
5
+ function mergeConfig(...objList) {
6
+ const clone = {};
7
+ objList.forEach((obj) => {
8
+ if (obj) Object.keys(obj).forEach((key) => {
9
+ const val = obj[key];
10
+ if (val !== void 0) clone[key] = val;
11
+ });
12
+ });
13
+ return clone;
14
+ }
15
+ function useNotification(rootConfig = {}) {
16
+ const { getContainer = defaultGetContainer, motion, prefixCls, maxCount, className, style, onAllRemoved, stack, renderNotifications,...shareConfig } = rootConfig;
17
+ const container = shallowRef();
18
+ const notificationRef = shallowRef();
19
+ const contextHolder = () => createVNode(Notifications_default, {
20
+ "container": container.value,
21
+ "ref": notificationRef,
22
+ "prefixCls": prefixCls,
23
+ "motion": motion,
24
+ "maxCount": maxCount,
25
+ "className": className,
26
+ "style": style,
27
+ "onAllRemoved": onAllRemoved,
28
+ "stack": stack,
29
+ "renderNotifications": renderNotifications
30
+ }, null);
31
+ const taskQueue = shallowRef([]);
32
+ const api = {
33
+ open(config) {
34
+ const mergedConfig = mergeConfig(shareConfig, config);
35
+ if (mergedConfig.key === null || mergedConfig.key === void 0) {
36
+ mergedConfig.key = `vc-notification-${uniqueKey}`;
37
+ uniqueKey += 1;
38
+ }
39
+ taskQueue.value = [...taskQueue.value, {
40
+ type: "open",
41
+ config: mergedConfig
42
+ }];
43
+ },
44
+ close(key) {
45
+ taskQueue.value = [...taskQueue.value, {
46
+ type: "close",
47
+ key
48
+ }];
49
+ },
50
+ destroy() {
51
+ taskQueue.value = [...taskQueue.value, { type: "destroy" }];
52
+ }
53
+ };
54
+ onMounted(() => {
55
+ container.value = getContainer();
56
+ });
57
+ watch(taskQueue, () => {
58
+ if (notificationRef.value && taskQueue.value.length) {
59
+ taskQueue.value.forEach((task) => {
60
+ switch (task.type) {
61
+ case "open":
62
+ notificationRef.value?.open(task.config);
63
+ break;
64
+ case "close":
65
+ notificationRef.value?.close(task.key);
66
+ break;
67
+ case "destroy":
68
+ notificationRef.value?.destroy();
69
+ break;
70
+ default: break;
71
+ }
72
+ });
73
+ taskQueue.value = taskQueue.value.filter((task) => !taskQueue.value.includes(task));
74
+ }
75
+ });
76
+ return [api, contextHolder];
77
+ }
78
+ export { useNotification as default };
@@ -0,0 +1,24 @@
1
+ Object.defineProperty(exports, "__esModule", { value: true });
2
+ const require_rolldown_runtime = require("../_virtual/rolldown_runtime.cjs");
3
+ let vue = require("vue");
4
+ var DEFAULT_OFFSET = 8;
5
+ var DEFAULT_THRESHOLD = 3;
6
+ var DEFAULT_GAP = 16;
7
+ var useStack = (config) => {
8
+ const result = (0, vue.reactive)({
9
+ offset: DEFAULT_OFFSET,
10
+ threshold: DEFAULT_THRESHOLD,
11
+ gap: DEFAULT_GAP
12
+ });
13
+ (0, vue.watchEffect)(() => {
14
+ const _config = (0, vue.unref)(config);
15
+ if (_config && typeof _config === "object") {
16
+ result.offset = _config.offset ?? DEFAULT_OFFSET;
17
+ result.threshold = _config.threshold ?? DEFAULT_THRESHOLD;
18
+ result.gap = _config.gap ?? DEFAULT_GAP;
19
+ }
20
+ });
21
+ return [(0, vue.computed)(() => !!(0, vue.unref)(config)), (0, vue.toRefs)(result)];
22
+ };
23
+ var useStack_default = useStack;
24
+ exports.default = useStack_default;
@@ -0,0 +1,6 @@
1
+ import { ComputedRef, MaybeRef, ToRefs } from 'vue';
2
+ import { StackConfig } from '../interface';
3
+ type StackParams = Exclude<StackConfig, boolean>;
4
+ type UseStack = (config?: MaybeRef<StackConfig | undefined>) => [ComputedRef<boolean>, ToRefs<StackParams>];
5
+ declare const useStack: UseStack;
6
+ export default useStack;
@@ -0,0 +1,22 @@
1
+ import { computed, reactive, toRefs, unref, watchEffect } from "vue";
2
+ var DEFAULT_OFFSET = 8;
3
+ var DEFAULT_THRESHOLD = 3;
4
+ var DEFAULT_GAP = 16;
5
+ var useStack = (config) => {
6
+ const result = reactive({
7
+ offset: DEFAULT_OFFSET,
8
+ threshold: DEFAULT_THRESHOLD,
9
+ gap: DEFAULT_GAP
10
+ });
11
+ watchEffect(() => {
12
+ const _config = unref(config);
13
+ if (_config && typeof _config === "object") {
14
+ result.offset = _config.offset ?? DEFAULT_OFFSET;
15
+ result.threshold = _config.threshold ?? DEFAULT_THRESHOLD;
16
+ result.gap = _config.gap ?? DEFAULT_GAP;
17
+ }
18
+ });
19
+ return [computed(() => !!unref(config)), toRefs(result)];
20
+ };
21
+ var useStack_default = useStack;
22
+ export { useStack_default as default };
package/dist/index.cjs ADDED
@@ -0,0 +1,6 @@
1
+ const require_Notice = require("./Notice.cjs");
2
+ const require_NotificationProvider = require("./NotificationProvider.cjs");
3
+ const require_useNotification = require("./hooks/useNotification.cjs");
4
+ exports.Notice = require_Notice.default;
5
+ exports.useNotification = require_useNotification.default;
6
+ exports.useNotificationProvider = require_NotificationProvider.useNotificationProvider;
@@ -0,0 +1,6 @@
1
+ import { NotificationAPI, NotificationConfig, default as useNotification } from './hooks/useNotification';
2
+ import { default as Notice } from './Notice';
3
+ import { useNotificationProvider } from './NotificationProvider';
4
+ export { Notice, useNotification, useNotificationProvider };
5
+ export type { NotificationAPI, NotificationConfig };
6
+ export type { NoticeProps } from './Notice';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import Notice_default from "./Notice.js";
2
+ import { useNotificationProvider } from "./NotificationProvider.js";
3
+ import useNotification from "./hooks/useNotification.js";
4
+ export { Notice_default as Notice, useNotification, useNotificationProvider };
File without changes
@@ -0,0 +1,55 @@
1
+ import { VueNode } from '@v-c/util/dist/type';
2
+ import { CSSProperties } from 'vue';
3
+ export type Placement = 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight';
4
+ type NoticeSemanticProps = 'wrapper';
5
+ export type Key = string | number;
6
+ export interface NoticeConfig {
7
+ content?: VueNode;
8
+ duration?: number | false | null;
9
+ showProgress?: boolean;
10
+ pauseOnHover?: boolean;
11
+ closeIcon?: VueNode;
12
+ closable?: boolean | ({
13
+ closeIcon?: VueNode;
14
+ onClose?: VoidFunction;
15
+ } & Record<string, any>);
16
+ className?: string;
17
+ style?: CSSProperties;
18
+ classNames?: {
19
+ [key in NoticeSemanticProps]?: string;
20
+ };
21
+ styles?: {
22
+ [key in NoticeSemanticProps]?: CSSProperties;
23
+ };
24
+ /** @private Internal usage. Do not override in your code */
25
+ props?: Record<string, any>;
26
+ onClose?: VoidFunction;
27
+ onClick?: (event: Event) => void;
28
+ }
29
+ export interface OpenConfig extends NoticeConfig {
30
+ key: Key;
31
+ placement?: Placement;
32
+ content?: VueNode;
33
+ duration?: number | false | null;
34
+ }
35
+ export type InnerOpenConfig = OpenConfig & {
36
+ times?: number;
37
+ };
38
+ export type Placements = Partial<Record<Placement, OpenConfig[]>>;
39
+ export type StackConfig = boolean | {
40
+ /**
41
+ * When number is greater than threshold, notifications will be stacked together.
42
+ * @default 3
43
+ */
44
+ threshold?: number;
45
+ /**
46
+ * Offset when notifications are stacked together.
47
+ * @default 8
48
+ */
49
+ offset?: number;
50
+ /**
51
+ * Spacing between each notification when expanded.
52
+ */
53
+ gap?: number;
54
+ };
55
+ export {};
File without changes
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import { h } from 'vue'
3
+ import { useNotification } from '../src'
4
+ import motion from './motion'
5
+
6
+ const [{ open }, holder] = useNotification({ motion })
7
+ const NOTICE = {
8
+ content: h('span', ['simple show']),
9
+ onclose() {
10
+ console.log('simple close')
11
+ },
12
+ }
13
+ function handleClick() {
14
+ open({
15
+ ...NOTICE,
16
+ props: {
17
+ 'data-testid': 'my-data-testid',
18
+ },
19
+ })
20
+ }
21
+ </script>
22
+
23
+ <template>
24
+ <div>
25
+ <button @click="handleClick">
26
+ simple show
27
+ </button>
28
+ <holder />
29
+ </div>
30
+ </template>
31
+
32
+ <style lang="less">
33
+ @import "./index.less";
34
+ </style>
package/docs/hooks.vue ADDED
@@ -0,0 +1,89 @@
1
+ <script setup lang="ts">
2
+ import { useNotification } from '../src'
3
+ import motion from './motion'
4
+
5
+ const [notice, contextHolder] = useNotification({ motion, closable: true })
6
+ </script>
7
+
8
+ <template>
9
+ <div>
10
+ <!-- Default -->
11
+ <button
12
+ @click="() => {
13
+ notice.open({
14
+ content: `${new Date().toISOString()}`,
15
+ })
16
+ }"
17
+ >
18
+ Basic
19
+ </button>
20
+ <button
21
+ @click="() => {
22
+ notice.open({
23
+ content: `${Array(Math.round(Math.random() * 5) + 1)
24
+ .fill(1)
25
+ .map(() => new Date().toISOString())
26
+ .join('\n')}`,
27
+ duration: null,
28
+ })
29
+ }"
30
+ >
31
+ Not Auto Close
32
+ </button>
33
+
34
+ <button
35
+ @click="() => {
36
+ notice.open({
37
+ content: `${Array(5)
38
+ .fill(1)
39
+ .map(() => new Date().toISOString())
40
+ .join('\n')}`,
41
+ duration: null,
42
+ });
43
+ }"
44
+ >
45
+ Not Auto Close
46
+ </button>
47
+ </div>
48
+
49
+ <div>
50
+ <button
51
+ @click="() => {
52
+ notice.open({
53
+ content: `No Close! ${new Date().toISOString()}`,
54
+ duration: null,
55
+ closable: false,
56
+ key: 'No Close',
57
+ onClose: () => {
58
+ console.log('Close!!!');
59
+ },
60
+ });
61
+ }"
62
+ >
63
+ No Closable
64
+ </button>
65
+
66
+ <button
67
+ @click="() => {
68
+ notice.close('No Close');
69
+ }"
70
+ >
71
+ Force Close No Closable
72
+ </button>
73
+ </div>
74
+
75
+ <div>
76
+ <button
77
+ @click="() => {
78
+ notice.destroy();
79
+ }"
80
+ >
81
+ Destroy All
82
+ </button>
83
+ </div>
84
+ <contextHolder />
85
+ </template>
86
+
87
+ <style scoped>
88
+
89
+ </style>
@@ -0,0 +1,265 @@
1
+ @notificationPrefixCls: vc-notification;
2
+
3
+ .@{notificationPrefixCls} {
4
+ // ====================== Notification ======================
5
+ position: fixed;
6
+ z-index: 1000;
7
+ display: flex;
8
+ max-height: 100vh;
9
+ padding: 10px;
10
+ align-items: flex-end;
11
+ width: 340px;
12
+ overflow-x: hidden;
13
+ overflow-y: auto;
14
+ height: 100vh;
15
+ box-sizing: border-box;
16
+ pointer-events: none;
17
+ flex-direction: column;
18
+
19
+ // Position
20
+ &-top,
21
+ &-topLeft,
22
+ &-topRight {
23
+ top: 0;
24
+ }
25
+
26
+ &-bottom,
27
+ &-bottomRight,
28
+ &-bottomLeft {
29
+ bottom: 0;
30
+ }
31
+
32
+ &-bottomRight,
33
+ &-topRight {
34
+ right: 0;
35
+ }
36
+
37
+ // ========================= Notice =========================
38
+ &-notice {
39
+ position: relative;
40
+ display: block;
41
+ box-sizing: border-box;
42
+ line-height: 1.5;
43
+ width: 100%;
44
+
45
+ &-wrapper {
46
+ pointer-events: auto;
47
+ position: relative;
48
+ display: block;
49
+ box-sizing: border-box;
50
+ border-radius: 3px 3px;
51
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
52
+ margin: 0 0 16px;
53
+ border: 1px solid #999;
54
+ border: 0px solid rgba(0, 0, 0, 0);
55
+ background: #fff;
56
+ width: 300px;
57
+ }
58
+
59
+ // Content
60
+ &-content {
61
+ padding: 7px 20px 7px 10px;
62
+ }
63
+
64
+ &-closable &-content {
65
+ padding-right: 20px;
66
+ }
67
+
68
+ &-close {
69
+ position: absolute;
70
+ top: 3px;
71
+ right: 5px;
72
+ color: #000;
73
+ font-weight: 700;
74
+ font-size: 16px;
75
+ line-height: 1;
76
+ text-decoration: none;
77
+ text-shadow: 0 1px 0 #fff;
78
+ outline: none;
79
+ cursor: pointer;
80
+ opacity: 0.2;
81
+ filter: alpha(opacity=20);
82
+ border: 0;
83
+ background-color: #fff;
84
+
85
+ &-x:after {
86
+ content: '×';
87
+ }
88
+
89
+ &:hover {
90
+ text-decoration: none;
91
+ opacity: 1;
92
+ filter: alpha(opacity=100);
93
+ }
94
+ }
95
+
96
+ // Progress
97
+ &-progress {
98
+ position: absolute;
99
+ left: 3px;
100
+ right: 3px;
101
+ border-radius: 1px;
102
+ overflow: hidden;
103
+ appearance: none;
104
+ -webkit-appearance: none;
105
+ display: block;
106
+ inline-size: 100%;
107
+ block-size: 2px;
108
+ border: 0;
109
+
110
+ &,
111
+ &::-webkit-progress-bar {
112
+ background-color: rgba(0, 0, 0, 0.04);
113
+ }
114
+
115
+ &::-moz-progress-bar {
116
+ background-color: #31afff;
117
+ }
118
+
119
+ &::-webkit-progress-value {
120
+ background-color: #31afff;
121
+ }
122
+ }
123
+ }
124
+
125
+ &-fade {
126
+ overflow: hidden;
127
+ transition: all 0.3s;
128
+ }
129
+
130
+ &-fade-appear-prepare {
131
+ pointer-events: none;
132
+ opacity: 0 !important;
133
+ }
134
+
135
+ &-fade-appear-start {
136
+ transform: translateX(100%);
137
+ opacity: 0;
138
+ }
139
+
140
+ &-fade-appear-active {
141
+ transform: translateX(0);
142
+ opacity: 1;
143
+ }
144
+
145
+ // .fade-effect() {
146
+ // animation-duration: 0.3s;
147
+ // animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
148
+ // animation-fill-mode: both;
149
+ // }
150
+
151
+ // &-fade-appear,
152
+ // &-fade-enter {
153
+ // opacity: 0;
154
+ // animation-play-state: paused;
155
+ // .fade-effect();
156
+ // }
157
+
158
+ // &-fade-leave {
159
+ // .fade-effect();
160
+ // animation-play-state: paused;
161
+ // }
162
+
163
+ // &-fade-appear&-fade-appear-active,
164
+ // &-fade-enter&-fade-enter-active {
165
+ // animation-name: rcNotificationFadeIn;
166
+ // animation-play-state: running;
167
+ // }
168
+
169
+ // &-fade-leave&-fade-leave-active {
170
+ // animation-name: rcDialogFadeOut;
171
+ // animation-play-state: running;
172
+ // }
173
+
174
+ // @keyframes rcNotificationFadeIn {
175
+ // 0% {
176
+ // opacity: 0;
177
+ // }
178
+ // 100% {
179
+ // opacity: 1;
180
+ // }
181
+ // }
182
+
183
+ // @keyframes rcDialogFadeOut {
184
+ // 0% {
185
+ // opacity: 1;
186
+ // }
187
+ // 100% {
188
+ // opacity: 0;
189
+ // }
190
+ // }
191
+
192
+ // ========================= Stack =========================
193
+ &-stack {
194
+ & > .@{notificationPrefixCls}-notice {
195
+ &-wrapper {
196
+ transition: all 0.3s;
197
+ position: absolute;
198
+ top: 12px;
199
+ opacity: 1;
200
+
201
+ &:not(:nth-last-child(-n + 3)) {
202
+ opacity: 0;
203
+ right: 34px;
204
+ width: 252px;
205
+ overflow: hidden;
206
+ color: transparent;
207
+ pointer-events: none;
208
+ }
209
+
210
+ &:nth-last-child(1) {
211
+ right: 10px;
212
+ }
213
+
214
+ &:nth-last-child(2) {
215
+ right: 18px;
216
+ width: 284px;
217
+ color: transparent;
218
+ overflow: hidden;
219
+ }
220
+
221
+ &:nth-last-child(3) {
222
+ right: 26px;
223
+ width: 268px;
224
+ color: transparent;
225
+ overflow: hidden;
226
+ }
227
+ }
228
+ }
229
+
230
+ &&-expanded {
231
+ & > .@{notificationPrefixCls}-notice {
232
+ &-wrapper {
233
+ &:not(:nth-last-child(-n + 1)) {
234
+ opacity: 1;
235
+ width: 300px;
236
+ right: 10px;
237
+ overflow: unset;
238
+ color: inherit;
239
+ pointer-events: auto;
240
+ }
241
+
242
+ &::before {
243
+ content: "";
244
+ position: absolute;
245
+ left: 0;
246
+ right: 0;
247
+ top: -16px;
248
+ width: 100%;
249
+ height: calc(100% + 32px);
250
+ background: transparent;
251
+ pointer-events: auto;
252
+ color: rgb(0,0,0);
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ &.@{notificationPrefixCls}-bottomRight {
259
+ & > .@{notificationPrefixCls}-notice-wrapper {
260
+ top: unset;
261
+ bottom: 12px;
262
+ }
263
+ }
264
+ }
265
+ }
@@ -0,0 +1,24 @@
1
+ <script setup lang="ts">
2
+ import { useNotification } from '../src'
3
+ import motion from './motion.ts'
4
+ import './index.less'
5
+
6
+ const [notice, contextHolder] = useNotification({ maxCount: 3, motion })
7
+ </script>
8
+
9
+ <template>
10
+ <button
11
+ @click="() => {
12
+ notice.open({
13
+ content: `${new Date().toISOString()}`,
14
+ });
15
+ }"
16
+ >
17
+ Max Count
18
+ </button>
19
+ <contextHolder />
20
+ </template>
21
+
22
+ <style scoped>
23
+
24
+ </style>
package/docs/motion.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { TransitionGroupProps } from 'vue'
2
+
3
+ const motion: TransitionGroupProps = {
4
+ appear: true,
5
+ name: 'vc-notification-fade',
6
+ // enterFromClass: 'vc-notification-fade-appear-start vc-notification-fade-appear-prepare',
7
+ // enterActiveClass: 'vc-notification-fade',
8
+ // enterToClass: 'vc-notification-fade-appear-active',
9
+ // leaveFromClass: 'vc-notification-fade-appear-active',
10
+ // leaveToClass: 'vc-notification-fade-appear-start vc-notification-fade-appear-prepare',
11
+ // leaveActiveClass: 'vc-notification-fade vc-notification-fade-appear-leave',
12
+ // moveClass: 'vc-notification-fade',
13
+ onBeforeLeave: (el) => {
14
+ const _el = el as HTMLDivElement
15
+ _el.style.height = `${_el.offsetHeight}px`
16
+ },
17
+ onLeave(el) {
18
+ setTimeout(() => {
19
+ const _el = el as HTMLDivElement
20
+ _el.style.height = '0px'
21
+ _el.style.opacity = '1'
22
+ _el.style.margin = '0px'
23
+ })
24
+ },
25
+ onAfterLeave(el) {
26
+ const _el = el as HTMLDivElement
27
+ _el.style.height = ''
28
+ _el.style.opacity = ''
29
+ _el.style.margin = ''
30
+ },
31
+ }
32
+
33
+ export default motion
@@ -0,0 +1,31 @@
1
+ <script setup lang="ts">
2
+ import Context from './context.vue'
3
+ import Hooks from './hooks.vue'
4
+ import MaxCount from './maxCount.vue'
5
+ import ShowProgress from './showProgress.vue'
6
+ import Stack from './stack.vue'
7
+ </script>
8
+
9
+ <template>
10
+ <Story title="Notification">
11
+ <Variant title="Context">
12
+ <Context />
13
+ </Variant>
14
+
15
+ <Variant title="Hooks">
16
+ <Hooks />
17
+ </Variant>
18
+
19
+ <Variant title="MaxCount">
20
+ <MaxCount />
21
+ </Variant>
22
+
23
+ <Variant title="ShowProgress">
24
+ <ShowProgress />
25
+ </Variant>
26
+
27
+ <Variant title="Stack">
28
+ <Stack />
29
+ </Variant>
30
+ </Story>
31
+ </template>