vueless 0.0.51 → 0.0.52

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 (33) hide show
  1. package/adatper.locale/locales/en.js +2 -0
  2. package/index.js +9 -0
  3. package/package.json +4 -6
  4. package/service.ui/index.js +1 -0
  5. package/ui.button/composables/attrs.composable.js +9 -2
  6. package/ui.button-link/composables/attrs.composable.js +23 -3
  7. package/ui.button-toggle/configs/default.config.js +1 -0
  8. package/ui.container-modal-confirm/composable/attrs.composable.js +55 -5
  9. package/ui.form-calendar/configs/default.config.js +21 -21
  10. package/ui.form-color-picker/composables/attrs.composable.js +2 -9
  11. package/ui.form-date-picker/configs/default.config.js +1 -1
  12. package/ui.form-date-picker-range/configs/default.config.js +16 -16
  13. package/ui.form-input/configs/default.config.js +2 -2
  14. package/ui.form-radio-card/composables/attrs.composable.js +2 -2
  15. package/ui.form-select/configs/default.config.js +2 -2
  16. package/ui.form-textarea/configs/default.config.js +1 -1
  17. package/ui.loader-rendering/composables/useLoaderRendering.js +19 -0
  18. package/ui.loader-rendering/index.vue +15 -16
  19. package/ui.navigation-pagination/configs/default.config.js +2 -2
  20. package/ui.notify/Docs.mdx +84 -0
  21. package/ui.notify/composables/attrs.composable.js +30 -0
  22. package/ui.notify/configs/default.config.js +47 -0
  23. package/ui.notify/constants/index.js +19 -0
  24. package/ui.notify/index.stories.js +77 -0
  25. package/ui.notify/index.vue +196 -214
  26. package/ui.notify/services/index.js +65 -63
  27. package/ui.other-loader-top/composables/useLoaderTop.js +57 -0
  28. package/ui.other-loader-top/index.vue +12 -13
  29. package/ui.text-alert/composables/attrs.composable.js +1 -1
  30. package/web-types.json +21 -3
  31. package/ui.loader-rendering/store/index.js +0 -17
  32. package/ui.notify/index.sto_ries.js +0 -102
  33. package/ui.other-loader-top/store/index.js +0 -59
@@ -0,0 +1,30 @@
1
+ import useUI from "../../composable.ui";
2
+
3
+ import defaultConfig from "../configs/default.config";
4
+
5
+ export function useAttrs(props) {
6
+ const { config, getAttrs } = useUI(defaultConfig, () => props.config);
7
+
8
+ const wrapperAttrs = getAttrs("wrapper");
9
+ const bodyAttrs = getAttrs("body");
10
+ const contentAttrs = getAttrs("content");
11
+ const labelAttrs = getAttrs("label");
12
+ const descriptionAttrs = getAttrs("description");
13
+ const iconSuccessAttrs = getAttrs("iconSuccess");
14
+ const iconWarningAttrs = getAttrs("iconWarning");
15
+ const iconErrorAttrs = getAttrs("iconError");
16
+ const iconCloseAttrs = getAttrs("iconClose");
17
+
18
+ return {
19
+ config,
20
+ wrapperAttrs,
21
+ bodyAttrs,
22
+ contentAttrs,
23
+ labelAttrs,
24
+ descriptionAttrs,
25
+ iconSuccessAttrs,
26
+ iconWarningAttrs,
27
+ iconCloseAttrs,
28
+ iconErrorAttrs,
29
+ };
30
+ }
@@ -0,0 +1,47 @@
1
+ export default /*tw*/ {
2
+ wrapper: "absolute overflow-visible md:w-[22rem]",
3
+ body: `
4
+ mb-3 flex w-fit items-center justify-center gap-3 rounded-2xl bg-gray-900/90
5
+ p-4 shadow-[0_4px_16px_rgba(17,24,39,0.5)] backdrop-blur-md md:shadow-[0_0px_12px_rgba(0,0,0,0.25)]
6
+ `,
7
+ bodySuccess: "bg-[radial-gradient(100.16%_500.78%_at_0%_50%,rgba(74,222,128,0.1)_2.17%,transparent)]",
8
+ bodyWarning: "bg-[radial-gradient(100.16%_500.78%_at_0%_50%,rgba(251,146,60,0.2)_2.17%,transparent)]",
9
+ bodyError: "bg-[radial-gradient(100.16%_500.78%_at_0%_50%,rgba(251,113,133,0.2)_2.17%,transparent)]",
10
+ content: "w-full flex flex-col max-w-full px-3 text-sm text-gray-200",
11
+ label: "mb-1 font-medium",
12
+ description: "break-words font-normal leading-5",
13
+ iconSuccess: "",
14
+ iconSuccessName: "check_circle",
15
+ iconWarning: "",
16
+ iconWarningName: "warning",
17
+ iconError: "",
18
+ iconErrorName: "error",
19
+ iconClose: "",
20
+ iconCloseName: "close",
21
+ transitionGroup: {
22
+ moveClass: "transition-all duration-500",
23
+ enterActiveClass: "transition-all duration-500",
24
+ leaveActiveClass: "transition-all duration-500 absolute",
25
+ enterFromClass: "opacity-0",
26
+ leaveToClass: "opacity-0",
27
+ },
28
+ duration: {
29
+ short: 4000,
30
+ medium: 8000,
31
+ long: 12000,
32
+ },
33
+ i18n: {
34
+ success: "Operation successful.",
35
+ warning: "Operation warning.",
36
+ error: "Operation error.",
37
+ },
38
+ positionClasses: {
39
+ page: "UNotifyPage",
40
+ aside: "UNotifyAside",
41
+ },
42
+ defaultVariants: {
43
+ xPosition: "center",
44
+ yPosition: "top",
45
+ vHtml: false,
46
+ },
47
+ };
@@ -0,0 +1,19 @@
1
+ /*
2
+ This const is needed to prevent the issue in script setup:
3
+ `defineProps` is referencing locally declared variables. (vue/valid-define-props)
4
+ */
5
+ export const UNotify = "UNotify";
6
+
7
+ export const NOTIFY_TYPE = {
8
+ success: "success",
9
+ warning: "warning",
10
+ error: "error",
11
+ };
12
+
13
+ export const POSITION = {
14
+ left: "left",
15
+ right: "right",
16
+ top: "top",
17
+ bottom: "bottom",
18
+ center: "center",
19
+ };
@@ -0,0 +1,77 @@
1
+ import { notify } from "../ui.notify/services";
2
+
3
+ import UNotify from "../ui.notify";
4
+ import UButton from "../ui.button";
5
+
6
+ import { getArgTypes } from "../service.storybook";
7
+
8
+ export default {
9
+ title: "Other / Notify",
10
+ component: UNotify,
11
+ args: {
12
+ type: "success",
13
+ label: "",
14
+ text: "",
15
+ },
16
+ argTypes: {
17
+ ...getArgTypes(UNotify.name),
18
+ },
19
+ };
20
+
21
+ const DefaultTemplate = (args) => ({
22
+ components: { UNotify, UButton },
23
+ setup() {
24
+ return { args };
25
+ },
26
+ template: `
27
+ <div>
28
+ <UNotify v-bind="args" />
29
+
30
+ <UButton label="show notify" @click="onClick"/>
31
+ </div>
32
+ `,
33
+
34
+ methods: {
35
+ onClick() {
36
+ const settings = {
37
+ type: "success",
38
+ label: "Some label",
39
+ };
40
+
41
+ notify(settings);
42
+ },
43
+ },
44
+ });
45
+
46
+ const TypesTemplate = (args) => ({
47
+ components: { UNotify, UButton },
48
+ setup() {
49
+ return { args };
50
+ },
51
+ template: `
52
+ <div class="flex gap-4">
53
+ <UNotify />
54
+
55
+ <UButton label="Success" @click="onClick('success')"/>
56
+ <UButton label="Warning" @click="onClick('warning')"/>
57
+ <UButton label="Error" @click="onClick('error')"/>
58
+ </div>
59
+ `,
60
+
61
+ methods: {
62
+ onClick(type) {
63
+ const settings = { type };
64
+
65
+ notify(settings);
66
+ },
67
+ },
68
+ });
69
+
70
+ export const Default = DefaultTemplate.bind({});
71
+ Default.args = {};
72
+
73
+ export const position = DefaultTemplate.bind({});
74
+ position.args = { xPosition: "right", yPosition: "bottom" };
75
+
76
+ export const types = TypesTemplate.bind({});
77
+ types.args = {};
@@ -1,248 +1,230 @@
1
1
  <template>
2
- <notifications
3
- group="notify"
4
- class="vueless-notify mt-safe-top !top-3 !w-full px-3 max-md:!left-0 md:!w-[22rem] md:px-0 lg:!top-4 lg:!mt-0"
5
- :position="position"
2
+ <TransitionGroup
3
+ ref="notificationsWrapperRef"
4
+ :style="notifyPositionStyles"
5
+ tag="div"
6
+ v-bind="{ ...config.transitionGroup, ...wrapperAttrs }"
6
7
  >
7
- <template #body="{ item, close: onClickClose }">
8
- <div :data-cy="`${item.type}-notify`" class="notify-body" :class="[item.type]">
9
- <UIcon
10
- v-if="isSuccessType"
11
- data-cy="type-notify"
12
- class="type-icon"
13
- name="check_circle"
14
- size="md"
15
- />
16
-
17
- <UIcon
18
- v-else-if="isWarningType"
19
- data-cy="type-notify"
20
- class="type-icon"
21
- name="warning"
22
- size="md"
23
- />
24
-
25
- <UIcon
26
- v-else-if="isErrorType"
27
- data-cy="type-notify"
28
- class="type-icon"
29
- name="error"
30
- size="md"
31
- />
32
-
33
- <div class="color-white w-full max-w-full px-3 text-sm text-gray-200">
34
- <div v-if="item.title" class="mb-1 font-medium" v-text="$t(item.title)" />
35
- <div
36
- class="break-words font-normal leading-5"
37
- v-html="prepareMessage(item.data.code, item.type, item.text)"
38
- />
39
- </div>
40
-
41
- <UIcon
42
- data-cy="close-notify"
43
- class="icon icon-close"
44
- name="close"
45
- size="xs"
46
- interactive
47
- @click="onClickClose"
48
- />
8
+ <div
9
+ v-for="notification in notifications"
10
+ :key="notification.id"
11
+ v-bind="bodyAttrs"
12
+ :class="getNotificationClasses(notification)"
13
+ >
14
+ <UIcon
15
+ v-if="notification.type === NOTIFY_TYPE.success"
16
+ data-cy="type-notify"
17
+ color="green"
18
+ variant="light"
19
+ size="md"
20
+ :name="config.iconSuccessName"
21
+ v-bind="iconSuccessAttrs"
22
+ />
23
+
24
+ <UIcon
25
+ v-else-if="notification.type === NOTIFY_TYPE.warning"
26
+ data-cy="type-notify"
27
+ color="orange"
28
+ variant="light"
29
+ size="md"
30
+ :name="config.iconWarningName"
31
+ v-bind="iconWarningAttrs"
32
+ />
33
+
34
+ <UIcon
35
+ v-else-if="notification.type === NOTIFY_TYPE.error"
36
+ data-cy="type-notify"
37
+ color="red"
38
+ variant="light"
39
+ size="md"
40
+ :name="config.iconErrorName"
41
+ v-bind="iconErrorAttrs"
42
+ />
43
+
44
+ <div v-bind="contentAttrs">
45
+ <template v-if="vHtml">
46
+ <span v-bind="labelAttrs" v-html="notification.label" />
47
+ <span v-bind="descriptionAttrs" v-html="getText(notification.text, notification.type)" />
48
+ </template>
49
+ <template v-else>
50
+ <span v-bind="labelAttrs" v-text="notification.label" />
51
+ <span v-bind="descriptionAttrs" v-text="getText(notification.text, notification.type)" />
52
+ </template>
49
53
  </div>
50
- </template>
51
- </notifications>
54
+
55
+ <UIcon
56
+ color="gray"
57
+ variant="light"
58
+ size="xs"
59
+ interactive
60
+ :name="config.iconCloseName"
61
+ v-bind="iconCloseAttrs"
62
+ @click="onClickClose(notification)"
63
+ />
64
+ </div>
65
+ </TransitionGroup>
52
66
  </template>
53
67
 
54
- <script>
55
- import { globalComponentConfig } from "../service.ui";
68
+ <script setup>
69
+ import { computed, onBeforeUnmount, onMounted, ref } from "vue";
70
+ import { merge } from "lodash-es";
56
71
 
57
- import UIcon from "../ui.image-icon";
72
+ import UIService, { globalComponentConfig, cx } from "../service.ui";
58
73
 
59
- const NOTIFY_TYPE = {
60
- success: "success",
61
- warning: "warning",
62
- error: "error",
63
- };
74
+ import { useLocale } from "../composable.locale";
75
+ import { useAttrs } from "./composables/attrs.composable";
64
76
 
65
- export default {
66
- name: "UNotify",
67
- components: {
68
- UIcon,
69
- },
77
+ import defaultConfig from "./configs/default.config";
78
+ import { UNotify, NOTIFY_TYPE, POSITION } from "./constants";
70
79
 
71
- props: {
72
- /**
73
- * A position on the x-axis.
74
- * @values left, center, right
75
- */
76
- xPosition: {
77
- type: String,
78
- default: "center",
79
- },
80
-
81
- /**
82
- * A position on the y-axis.
83
- * @values top, bottom
84
- */
85
- yPosition: {
86
- type: String,
87
- default: "top",
88
- },
89
- },
80
+ import UIcon from "../ui.image-icon";
90
81
 
91
- data() {
92
- return {
93
- type: null,
94
- };
82
+ /* Should be a string for correct web-types gen */
83
+ defineOptions({ name: "UNotify", inheritAttrs: false });
84
+
85
+ const props = defineProps({
86
+ /**
87
+ * A position on the x-axis.
88
+ * @values left, center, right
89
+ */
90
+ xPosition: {
91
+ type: String,
92
+ default: UIService.get(defaultConfig, UNotify).default.xPosition,
95
93
  },
96
94
 
97
- computed: {
98
- isSuccessType() {
99
- return this.type === NOTIFY_TYPE.success;
100
- },
101
-
102
- isWarningType() {
103
- return this.type === NOTIFY_TYPE.warning;
104
- },
105
-
106
- isErrorType() {
107
- return this.type === NOTIFY_TYPE.error;
108
- },
109
-
110
- i18n() {
111
- return {
112
- message: `${this.type}.default`,
113
- };
114
- },
115
-
116
- position() {
117
- return [this.yPosition, this.xPosition];
118
- },
95
+ /**
96
+ * A position on the y-axis.
97
+ * @values top, bottom
98
+ */
99
+ yPosition: {
100
+ type: String,
101
+ default: UIService.get(defaultConfig, UNotify).default.yPosition,
119
102
  },
120
103
 
121
- created() {
122
- window.addEventListener("resize", this.setPosition, { passive: true });
104
+ /**
105
+ * Use v-html to render notification label and description content.
106
+ */
107
+ vHtml: {
108
+ type: Boolean,
109
+ default: UIService.get(defaultConfig, UNotify).default.vHtml,
123
110
  },
124
111
 
125
- beforeUnmount() {
126
- window.removeEventListener("resize", this.setPosition);
112
+ /**
113
+ * Sets component ui config object.
114
+ */
115
+ config: {
116
+ type: Object,
117
+ default: () => ({}),
127
118
  },
119
+ });
120
+
121
+ const {
122
+ config,
123
+ wrapperAttrs,
124
+ bodyAttrs,
125
+ contentAttrs,
126
+ labelAttrs,
127
+ descriptionAttrs,
128
+ iconSuccessAttrs,
129
+ iconWarningAttrs,
130
+ iconErrorAttrs,
131
+ iconCloseAttrs,
132
+ } = useAttrs(props);
133
+
134
+ const { tm } = useLocale();
135
+
136
+ const notifications = ref([]);
137
+ const notifyPositionStyles = ref({});
138
+
139
+ const notificationsWrapperRef = ref(null);
140
+
141
+ const currentLocale = computed(() => merge(tm("UNotify"), props.config.i18n));
142
+
143
+ onMounted(() => {
144
+ window.addEventListener("resize", setPosition, { passive: true });
145
+ window.addEventListener("notifyStart", onNotifyStart);
146
+ window.addEventListener("notifyEnd", onNotifyEnd);
147
+ window.addEventListener("notifyClearAll", onClearAll);
148
+
149
+ setPosition();
150
+ });
151
+
152
+ onBeforeUnmount(() => {
153
+ window.removeEventListener("resize", setPosition);
154
+ window.removeEventListener("notifyStart", onNotifyStart);
155
+ window.removeEventListener("notifyEnd", onNotifyEnd);
156
+ window.removeEventListener("notifyClearAll", onClearAll);
157
+ });
158
+
159
+ function onNotifyStart(event) {
160
+ notifications.value.push({ ...event.detail });
161
+ }
128
162
 
129
- methods: {
130
- prepareMessage(code, type, text) {
131
- this.setPosition();
132
-
133
- this.type = type;
134
-
135
- if (text) {
136
- return text;
137
- }
138
-
139
- return this.i18n.message;
140
- },
141
-
142
- setPosition() {
143
- const getOffsetWidth = (className) =>
144
- className ? document.querySelector(`.${className}`)?.offsetWidth : 0;
145
-
146
- const NOTIFY_CLASS = "vueless-notify";
147
- const classes = globalComponentConfig.UNotify?.positionClasses;
148
- const pageWidth = getOffsetWidth(classes.page || "UNotifyPage");
149
- const asideWidth = getOffsetWidth(classes.aside || "UNotifyAside");
150
- const notifyWidth = getOffsetWidth(NOTIFY_CLASS);
163
+ function onNotifyEnd(event) {
164
+ notifications.value = notifications.value.filter(
165
+ (notification) => notification.id !== event.detail.id,
166
+ );
167
+ }
151
168
 
152
- if (!pageWidth) return;
169
+ function onClearAll() {
170
+ notifications.value = [];
171
+ }
153
172
 
154
- const leftShift = asideWidth + pageWidth / 2 - notifyWidth / 2;
173
+ function onClickClose(targetNotification) {
174
+ notifications.value = notifications.value.filter(
175
+ (notification) => notification.id !== targetNotification.id,
176
+ );
177
+ }
155
178
 
156
- document.querySelector(`.${NOTIFY_CLASS}`).style.setProperty("left", `${leftShift}px`);
157
- },
158
- },
159
- };
160
- </script>
179
+ function getOffsetWidth(selector) {
180
+ return document.querySelector(selector)?.offsetWidth || 0;
181
+ }
161
182
 
162
- <i18n>
163
- en:
164
- success:
165
- default: "Operation successful."
166
- error:
167
- default: "Operation error."
168
- warning:
169
- default: "Operation warning."
170
- ru:
171
- success:
172
- default: "Произошла ошибка."
173
- error:
174
- default: "Операция успешна."
175
- warning:
176
- default: "Предупреждение."
177
- ua:
178
- success:
179
- default: "Операція успішна."
180
- error:
181
- default: "Сталася помилка."
182
- warning:
183
- default: "Попередження."
184
- </i18n>
185
-
186
- <style lang="postcss" scoped>
187
- .vueless-notify {
188
- :deep(.vue-notification-wrapper) {
189
- @apply overflow-visible;
183
+ function setPosition() {
184
+ const positionClasses = globalComponentConfig.UNotify?.positionClasses;
185
+ const pageClass = positionClasses?.page || config.value.positionClasses.page;
186
+ const asideClass = positionClasses?.aside || config.value.positionClasses.aside;
187
+ const pageWidth = getOffsetWidth(`${pageClass}`);
188
+ const asideWidth = getOffsetWidth(`${asideClass}`);
189
+ const notifyWidth = notificationsWrapperRef.value.$el?.offsetWidth || 0;
190
+
191
+ const styles = {
192
+ left: "auto",
193
+ top: "auto",
194
+ right: "auto",
195
+ bottom: "auto",
196
+ };
197
+
198
+ styles[props.yPosition] = "0px";
199
+
200
+ if (props.xPosition === POSITION.center) {
201
+ styles.left = `calc(50% - ${notifyWidth / 2}px)`;
202
+ } else {
203
+ styles[props.xPosition] = "0px";
190
204
  }
191
205
 
192
- :deep(.notify-body) {
193
- @apply rounded-2xl backdrop-blur-md;
194
- @apply flex items-center;
195
- @apply mb-3 p-4 shadow-[0_4px_16px_rgba(17,24,39,0.5)] md:shadow-[0_0px_12px_rgba(0,0,0,0.25)];
196
-
197
- .icon-close {
198
- path {
199
- @apply fill-current text-gray-400;
200
- }
201
- }
206
+ if (pageWidth && props.xPosition !== POSITION.right) {
207
+ styles.left = `${asideWidth + pageWidth / 2 - notifyWidth / 2}px`;
202
208
  }
203
209
 
204
- :deep(.warning) {
205
- background: radial-gradient(
206
- 100.16% 500.78% at 0% 50%,
207
- rgba(251, 146, 60, 0.2) 2.17%,
208
- transparent
209
- );
210
-
211
- .type-icon path {
212
- @apply fill-current text-orange-400;
213
- }
214
- }
210
+ notifyPositionStyles.value = styles;
211
+ }
215
212
 
216
- :deep(.success) {
217
- background: radial-gradient(
218
- 100.16% 500.78% at 0% 50%,
219
- rgba(74, 222, 128, 0.1) 2.17%,
220
- transparent
221
- );
213
+ function getText(notificationText, type) {
214
+ return notificationText || currentLocale.value[type];
215
+ }
222
216
 
223
- .type-icon path {
224
- @apply fill-current text-green-400;
225
- }
217
+ function getNotificationClasses(notification) {
218
+ if (notification.type === NOTIFY_TYPE.success) {
219
+ return cx([bodyAttrs.value.class, config.value.bodySuccess]);
226
220
  }
227
221
 
228
- :deep(.error) {
229
- background: radial-gradient(
230
- 100.16% 500.78% at 0% 50%,
231
- rgba(251, 113, 133, 0.2) 2.17%,
232
- transparent
233
- );
234
-
235
- .type-icon path {
236
- @apply fill-current text-red-400;
237
- }
222
+ if (notification.type === NOTIFY_TYPE.warning) {
223
+ return cx([bodyAttrs.value.class, config.value.bodyWarning]);
238
224
  }
239
225
 
240
- /*
241
- NOTE! This expression should be declared after all subclasses,
242
- otherwise we will get gradient with white background
243
- */
244
- :deep(.notify-body) {
245
- @apply bg-gray-900 bg-opacity-90;
226
+ if (notification.type === NOTIFY_TYPE.error) {
227
+ return cx([bodyAttrs.value.class, config.value.bodyError]);
246
228
  }
247
229
  }
248
- </style>
230
+ </script>