@volverjs/ui-vue 0.0.9-beta.3 → 0.0.9-beta.5

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 (32) hide show
  1. package/README.md +2 -2
  2. package/auto-imports.d.ts +2 -0
  3. package/dist/components/VvCombobox/VvCombobox.es.js +1 -1
  4. package/dist/components/VvCombobox/VvCombobox.umd.js +1 -1
  5. package/dist/components/VvDialog/VvDialog.es.js +15 -15
  6. package/dist/components/VvDialog/VvDialog.umd.js +1 -1
  7. package/dist/components/VvDropdown/VvDropdown.es.js +1 -1
  8. package/dist/components/VvDropdown/VvDropdown.umd.js +1 -1
  9. package/dist/components/index.es.js +15 -15
  10. package/dist/components/index.umd.js +1 -1
  11. package/dist/composables/alert/useAlert.d.ts +27 -0
  12. package/dist/composables/index.d.ts +2 -0
  13. package/dist/composables/index.es.js +81 -0
  14. package/dist/composables/index.umd.js +1 -0
  15. package/dist/constants.d.ts +10 -0
  16. package/dist/icons.es.js +3 -3
  17. package/dist/icons.umd.js +1 -1
  18. package/dist/stories/AlertGroup/AlertGroupSlots.stories.d.ts +2 -2
  19. package/dist/stories/AlertGroup/AlertGroupWithComposable.stories.d.ts +6 -0
  20. package/package.json +9 -1
  21. package/src/assets/icons/detailed.json +1 -1
  22. package/src/assets/icons/normal.json +1 -1
  23. package/src/assets/icons/simple.json +1 -1
  24. package/src/components/VvDialog/VvDialog.vue +14 -14
  25. package/src/components/VvDropdown/VvDropdown.vue +1 -1
  26. package/src/composables/alert/useAlert.ts +103 -0
  27. package/src/composables/index.ts +3 -0
  28. package/src/constants.ts +21 -0
  29. package/src/stories/AlertGroup/AlertGroup.test.ts +13 -0
  30. package/src/stories/AlertGroup/AlertGroupSlots.stories.ts +3 -3
  31. package/src/stories/AlertGroup/AlertGroupWithComposable.stories.ts +118 -0
  32. package/src/types/alert.d.ts +20 -0
@@ -15,16 +15,16 @@
15
15
  const dialogEl: Ref<HTMLDialogElement | undefined> = ref()
16
16
 
17
17
  // data
18
+ const modelValue = useVModel(props, 'modelValue', emit)
18
19
  const localModelValue = ref(false)
19
- const modelValue = computed({
20
- get() {
21
- return props.modelValue ?? localModelValue.value
22
- },
23
- set(value) {
24
- if (props.modelValue === undefined) {
25
- localModelValue.value = value
20
+ const opened = computed({
21
+ get: () => modelValue.value ?? localModelValue.value,
22
+ set: (newValue) => {
23
+ if (modelValue.value === undefined) {
24
+ localModelValue.value = newValue
25
+ return
26
26
  }
27
- emit('update:modelValue', value)
27
+ modelValue.value = newValue
28
28
  },
29
29
  })
30
30
 
@@ -80,24 +80,24 @@
80
80
 
81
81
  // methods
82
82
  onClickOutside(modalWrapper, () => {
83
- if (!props.keepOpen) {
84
- modelValue.value = false
83
+ if (!props.keepOpen && opened.value) {
84
+ opened.value = false
85
85
  }
86
86
  })
87
87
 
88
88
  function close() {
89
- modelValue.value = false
89
+ opened.value = false
90
90
  }
91
91
 
92
92
  function open() {
93
- modelValue.value = true
93
+ opened.value = true
94
94
  }
95
95
 
96
96
  defineExpose({ close, open })
97
97
 
98
98
  // keyboard
99
99
  onKeyStroke('Escape', (e) => {
100
- if (modelValue.value) {
100
+ if (opened.value) {
101
101
  e.preventDefault()
102
102
  close()
103
103
  }
@@ -107,7 +107,7 @@
107
107
  <template>
108
108
  <Transition :name="transitioName" v-on="dialogTransitionHandlers">
109
109
  <dialog
110
- v-show="modelValue"
110
+ v-show="opened"
111
111
  v-bind="dialogAttrs"
112
112
  ref="dialogEl"
113
113
  :class="dialogClass"
@@ -270,7 +270,7 @@
270
270
  onClickOutside(
271
271
  floatingEl,
272
272
  () => {
273
- if (!props.keepOpen) {
273
+ if (!props.keepOpen && expanded.value) {
274
274
  expanded.value = false
275
275
  }
276
276
  },
@@ -0,0 +1,103 @@
1
+ import {
2
+ DEFAULT_ALERT_AUTO_CLOSE,
3
+ DEFAULT_ALERT_DISMISSABLE,
4
+ DEFAULT_ALERT_GROUP,
5
+ DEFAULT_ALERT_MODIFIERS,
6
+ DEFAULT_ALERT_INFO_ICON,
7
+ DefaultAlertIconMap,
8
+ } from '@/constants'
9
+ import type { Alert, AlertModifiers } from '@/types/alert'
10
+
11
+ const groups = reactive(
12
+ new Map<string, Map<string, Alert>>([
13
+ [DEFAULT_ALERT_GROUP, new Map<string, Alert>()],
14
+ ]),
15
+ )
16
+
17
+ /**
18
+ * @description Composable to access alert groups, alerts and functions to add, remove and get alerts by group.
19
+ * @example
20
+ * const { groups, alerts, addAlert, removeAlert, getAlerts } = useAlert()
21
+ * addAlert({
22
+ * title: 'Success!',
23
+ * modifiers: 'success',
24
+ * })
25
+ *
26
+ * `<vv-alert-group :items="alerts" :onClose="removeAlert" />`
27
+ *
28
+ * @returns {
29
+ * alerts: ComputedRef<Alert[]> reactive list of alerts default group,
30
+ * groups: ReactiveRef<Map<string, Map<string, Alert>>>,
31
+ * addAlert: Function to add alert,
32
+ * removeAlert: Function to remove alert,
33
+ * getAlerts: Function to get alerts by group
34
+ * }
35
+ */
36
+ export const useAlert = () => {
37
+ const addAlert = (
38
+ {
39
+ id = crypto.randomUUID(),
40
+ group = DEFAULT_ALERT_GROUP,
41
+ title,
42
+ icon = DEFAULT_ALERT_INFO_ICON,
43
+ content,
44
+ footer,
45
+ modifiers = DEFAULT_ALERT_MODIFIERS,
46
+ dismissable = DEFAULT_ALERT_DISMISSABLE,
47
+ autoClose = DEFAULT_ALERT_AUTO_CLOSE,
48
+ } = {} as Partial<Alert>,
49
+ ) => {
50
+ if (!groups.has(group)) {
51
+ groups.set(group, new Map<string, Alert>())
52
+ }
53
+ const groupMap = groups.get(group)
54
+ const normalizedModifiers =
55
+ typeof modifiers === 'string' ? modifiers.split(' ') : modifiers
56
+
57
+ if (!icon) {
58
+ const alertModifier = normalizedModifiers.find((modifier) =>
59
+ DefaultAlertIconMap.has(modifier as AlertModifiers),
60
+ ) as AlertModifiers | undefined
61
+
62
+ if (alertModifier) {
63
+ icon = DefaultAlertIconMap.get(alertModifier) as string
64
+ }
65
+ }
66
+ groupMap?.set(id.toString(), {
67
+ id,
68
+ group,
69
+ title,
70
+ icon,
71
+ content,
72
+ footer,
73
+ modifiers,
74
+ dismissable,
75
+ autoClose,
76
+ timestamp: Date.now(),
77
+ })
78
+ }
79
+
80
+ const removeAlert = (id: string | number, group = DEFAULT_ALERT_GROUP) => {
81
+ const groupMap = groups.get(group)
82
+ groupMap?.delete(id.toString())
83
+ }
84
+
85
+ const getAlerts = (group = DEFAULT_ALERT_GROUP) => {
86
+ return computed(() => {
87
+ const groupMap = groups.get(group)
88
+ return groupMap && groupMap instanceof Map
89
+ ? Array.from(groupMap?.values()).sort(
90
+ (a, b) => a.timestamp - b.timestamp,
91
+ )
92
+ : []
93
+ })
94
+ }
95
+
96
+ return {
97
+ groups,
98
+ alerts: getAlerts(),
99
+ addAlert,
100
+ removeAlert,
101
+ getAlerts,
102
+ }
103
+ }
@@ -0,0 +1,3 @@
1
+ import { useAlert } from './alert/useAlert'
2
+
3
+ export { useAlert }
package/src/constants.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { InjectionKey, Ref } from 'vue'
2
2
  import type { Emitter } from 'mitt'
3
3
  import type { Volver } from './Volver'
4
+ import type { AlertModifiers } from './types/alert'
4
5
 
5
6
  export const DEFAULT_ICONIFY_PROVIDER = 'vv'
6
7
 
@@ -110,3 +111,23 @@ export const INJECTION_KEY_ALERT_GROUP = Symbol.for(
110
111
  name?: Ref<string | undefined>
111
112
  bus?: Emitter<{ close: string }>
112
113
  }>
114
+ export const DEFAULT_ALERT_AUTO_CLOSE = 10000
115
+ export const DEFAULT_ALERT_MODIFIERS = 'info'
116
+ export const DEFAULT_ALERT_DISMISSABLE = true
117
+ export const DEFAULT_ALERT_GROUP = 'default'
118
+ export const DEFAULT_ALERT_INFO_ICON = 'information'
119
+ export const DEFAULT_ALERT_SUCCESS_ICON = 'check-circle'
120
+ export const DEFAULT_ALERT_WARNING_ICON = 'warning'
121
+ export const DEFAULT_ALERT_DANGER_ICON = 'error'
122
+ export const DefaultAlertIconMap = new Map<AlertModifiers, string>([
123
+ ['success', DEFAULT_ALERT_SUCCESS_ICON],
124
+ ['info', DEFAULT_ALERT_INFO_ICON],
125
+ ['warning', DEFAULT_ALERT_WARNING_ICON],
126
+ ['danger', DEFAULT_ALERT_DANGER_ICON],
127
+ ])
128
+ // {
129
+ // success: DEFAULT_ALERT_SUCCESS_ICON,
130
+ // info: DEFAULT_ALERT_INFO_ICON,
131
+ // warning: DEFAULT_ALERT_WARNING_ICON,
132
+ // danger: DEFAULT_ALERT_DANGER_ICON,
133
+ // }
@@ -6,6 +6,8 @@ export async function defaultTest({ canvasElement, args }: PlayAttributes) {
6
6
  const element = (await within(canvasElement).findByTestId(
7
7
  'element',
8
8
  )) as HTMLElement
9
+ const buttons =
10
+ canvasElement.getElementsByClassName('buttons-container')?.[0]
9
11
 
10
12
  expect(element).toHaveClass('vv-alert-group')
11
13
 
@@ -71,6 +73,17 @@ export async function defaultTest({ canvasElement, args }: PlayAttributes) {
71
73
  expect(element.lastElementChild?.innerHTML).toEqual(div.innerHTML)
72
74
  }
73
75
 
76
+ if (buttons) {
77
+ // click every button child and expect to have alert with correct css class
78
+ for (let i = 0; i < buttons.children.length; i++) {
79
+ const button = buttons.children[i] as HTMLElement
80
+ await button.click()
81
+ expect(alertGroupList.lastElementChild).toHaveClass(
82
+ `vv-alert--${button.id}`,
83
+ )
84
+ }
85
+ }
86
+
74
87
  // check accessibility
75
88
  await expect(element).toHaveNoViolations()
76
89
  }
@@ -1,11 +1,11 @@
1
1
  import type { Meta } from '@storybook/vue3'
2
- import VvAlert from '@/components/VvAlert/VvAlert.vue'
2
+ import VvAlertGroup from '@/components/VvAlertGroup/VvAlertGroup.vue'
3
3
  import { defaultArgs, argTypes } from './AlertGroup.settings'
4
4
  import { Default as DefaultStory, type Story } from './AlertGroup.stories'
5
5
 
6
- const meta: Meta<typeof VvAlert> = {
6
+ const meta: Meta<typeof VvAlertGroup> = {
7
7
  title: 'Components/AlertGroup/Slots',
8
- component: VvAlert,
8
+ component: VvAlertGroup,
9
9
  args: defaultArgs,
10
10
  argTypes,
11
11
  }
@@ -0,0 +1,118 @@
1
+ import type { Meta } from '@storybook/vue3'
2
+ import VvAlertGroup from '@/components/VvAlertGroup/VvAlertGroup.vue'
3
+ import VvButton from '@/components/VvButton/VvButton.vue'
4
+ import { defaultArgs, argTypes } from './AlertGroup.settings'
5
+ import { Default as DefaultStory, type Story } from './AlertGroup.stories'
6
+ import { useAlert } from '@/composables/alert/useAlert'
7
+
8
+ const meta: Meta<typeof VvAlertGroup> = {
9
+ title: 'Components/AlertGroup/UseComposable',
10
+ component: VvAlertGroup,
11
+ args: defaultArgs,
12
+ argTypes,
13
+ tags: ['autodocs'],
14
+ }
15
+
16
+ export default meta
17
+
18
+ export const UseComposable: Story = {
19
+ ...DefaultStory,
20
+ parameters: {
21
+ docs: {
22
+ source: {
23
+ type: 'code',
24
+ language: 'html',
25
+ code: /* html */ `
26
+ <div class="flex gap-md">
27
+ <vv-button label="Show success" modifiers="secondary" @click="showSuccess" class="mb-lg" />
28
+ <vv-button label="Show danger" modifiers="secondary" @click="showDanger" class="mb-lg" />
29
+ <vv-button label="Show warning" modifiers="secondary" @click="showWarning" class="mb-lg" />
30
+ <vv-button label="Show info" modifiers="secondary" @click="showInfo" class="mb-lg" />
31
+ </div>
32
+ <vv-alert-group v-bind="args" :items="alerts" :onClose="removeAlert" data-testId="element" />
33
+
34
+ <script setup lang='ts'>
35
+ import { useAlert } from '@volverjs/ui-vue/composables'
36
+
37
+ const { addAlert, removeAlert, alerts } = useAlert()
38
+
39
+ function showSuccess() {
40
+ addAlert({
41
+ title: 'Success!',
42
+ modifiers: 'success',
43
+ })
44
+ }
45
+ function showDanger() {
46
+ addAlert({
47
+ title: 'Danger!',
48
+ modifiers: 'danger',
49
+ })
50
+ }
51
+ function showWarning() {
52
+ addAlert({
53
+ title: 'Warning!',
54
+ modifiers: 'warning',
55
+ })
56
+ }
57
+ function showInfo() {
58
+ addAlert({
59
+ title: 'Info!',
60
+ modifiers: 'info',
61
+ })
62
+ }
63
+ </script>
64
+ `,
65
+ },
66
+ },
67
+ },
68
+ render: (args) => ({
69
+ components: { VvAlertGroup, VvButton },
70
+ setup() {
71
+ const { addAlert, removeAlert, alerts } = useAlert()
72
+
73
+ function showSuccess() {
74
+ addAlert({
75
+ title: 'Success!',
76
+ modifiers: 'success',
77
+ })
78
+ }
79
+ function showDanger() {
80
+ addAlert({
81
+ title: 'Danger!',
82
+ modifiers: 'danger',
83
+ })
84
+ }
85
+ function showWarning() {
86
+ addAlert({
87
+ title: 'Warning!',
88
+ modifiers: 'warning',
89
+ })
90
+ }
91
+ function showInfo() {
92
+ addAlert({
93
+ title: 'Info!',
94
+ modifiers: 'info',
95
+ })
96
+ }
97
+
98
+ return {
99
+ args,
100
+ alerts,
101
+ removeAlert,
102
+ showSuccess,
103
+ showDanger,
104
+ showWarning,
105
+ showInfo,
106
+ }
107
+ },
108
+ template: /* html */ `
109
+ <div class="buttons-container flex gap-md" >
110
+ <vv-button id="success" label="Show success" modifiers="secondary" @click="showSuccess" class="mb-lg" />
111
+ <vv-button id="danger" label="Show danger" modifiers="secondary" @click="showDanger" class="mb-lg" />
112
+ <vv-button id="warning" label="Show warning" modifiers="secondary" @click="showWarning" class="mb-lg" />
113
+ <vv-button id="info" label="Show info" modifiers="secondary" @click="showInfo" class="mb-lg" />
114
+ </div>
115
+ <vv-alert-group v-bind="args" :items="alerts" :onClose="removeAlert" data-testId="element" />
116
+ `,
117
+ }),
118
+ }
@@ -0,0 +1,20 @@
1
+ export type AlertModifiers =
2
+ | 'success'
3
+ | 'info'
4
+ | 'warning'
5
+ | 'danger'
6
+ | 'brand'
7
+ | 'accent'
8
+
9
+ export type Alert = {
10
+ id: string | number
11
+ group: string
12
+ title?: string
13
+ icon: string | Record<string, unknown>
14
+ content?: string
15
+ footer?: string
16
+ modifiers: AlertModifiers
17
+ dismissable: boolean
18
+ autoClose: number
19
+ timestamp: number
20
+ }