frappe-ui 0.1.164 → 0.1.166
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.
- package/frappe/Billing/TrialBanner.vue +1 -2
- package/package.json +2 -1
- package/src/components/Sidebar/Sidebar.story.vue +79 -0
- package/src/components/Sidebar/Sidebar.vue +60 -0
- package/src/components/Sidebar/SidebarHeader.vue +55 -0
- package/src/components/Sidebar/SidebarItem.vue +94 -0
- package/src/components/Sidebar/SidebarSection.vue +69 -0
- package/src/components/Sidebar/index.ts +2 -0
- package/src/components/Sidebar/types.ts +31 -0
- package/src/components/TextEditor/video-extension.ts +0 -3
- package/src/components/Toast/Toast.vue +2 -3
- package/src/components/Toast/index.ts +8 -3
- package/src/index.ts +1 -0
|
@@ -61,8 +61,7 @@ createResource({
|
|
|
61
61
|
trialEndDays.value = calculateTrialEndDays(data.trial_end_date)
|
|
62
62
|
baseEndpoint.value = data.base_url
|
|
63
63
|
siteName.value = data.site_name
|
|
64
|
-
showBanner.value =
|
|
65
|
-
data.setup_complete && data.plan.is_trial_plan && trialEndDays.value > 0
|
|
64
|
+
showBanner.value = data.plan.is_trial_plan && trialEndDays.value > 0
|
|
66
65
|
},
|
|
67
66
|
})
|
|
68
67
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frappe-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.166",
|
|
4
4
|
"description": "A set of components and utilities for rapid UI development",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"@tiptap/vue-3": "^2.0.3",
|
|
58
58
|
"@vueuse/core": "^10.4.1",
|
|
59
59
|
"dayjs": "^1.11.13",
|
|
60
|
+
"dompurify": "^3.2.6",
|
|
60
61
|
"echarts": "^5.6.0",
|
|
61
62
|
"feather-icons": "^4.28.0",
|
|
62
63
|
"highlight.js": "^11.11.1",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { reactive } from 'vue';
|
|
3
|
+
import Sidebar from './Sidebar.vue';
|
|
4
|
+
|
|
5
|
+
import Notifications from '~icons/lucide/bell';
|
|
6
|
+
import Deals from '~icons/lucide/briefcase';
|
|
7
|
+
import Organizations from '~icons/lucide/building';
|
|
8
|
+
import Tasks from '~icons/lucide/check-square';
|
|
9
|
+
import Notes from '~icons/lucide/clipboard';
|
|
10
|
+
import Link from '~icons/lucide/link';
|
|
11
|
+
import EmailTemplates from '~icons/lucide/mail';
|
|
12
|
+
import Moon from '~icons/lucide/moon';
|
|
13
|
+
import CallLogs from '~icons/lucide/phone';
|
|
14
|
+
import Settings from '~icons/lucide/settings';
|
|
15
|
+
import User from '~icons/lucide/user';
|
|
16
|
+
import Contacts from '~icons/lucide/user-check';
|
|
17
|
+
import Leads from '~icons/lucide/users';
|
|
18
|
+
|
|
19
|
+
const crmSidebar = reactive({
|
|
20
|
+
header: {
|
|
21
|
+
title: 'Frappe CRM',
|
|
22
|
+
subtitle: 'Jane Doe',
|
|
23
|
+
menuItems: [
|
|
24
|
+
{ label: 'Toggle Theme', icon: Moon, onClick: toggleTheme },
|
|
25
|
+
{ label: 'Help', to: '/help', icon: Settings, onClick: () => alert('Help clicked!') },
|
|
26
|
+
{ label: 'Logout', to: '/logout', icon: User, onClick: () => alert('Logging out...') },
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
sections: [
|
|
30
|
+
{
|
|
31
|
+
label: '',
|
|
32
|
+
items: [
|
|
33
|
+
{ label: 'Notifications', icon: Notifications, to: '' },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: '',
|
|
38
|
+
items: [
|
|
39
|
+
{ label: 'Leads', icon: Leads, to: '/leads' },
|
|
40
|
+
{ label: 'Deals', icon: Deals, to: '/deals' },
|
|
41
|
+
{ label: 'Contacts', icon: Contacts, to: '/contacts' },
|
|
42
|
+
{ label: 'Organizations', icon: Organizations, to: '/organizations' },
|
|
43
|
+
{ label: 'Notes', icon: Notes, to: '/notes' },
|
|
44
|
+
{ label: 'Tasks', icon: Tasks, to: '/tasks' },
|
|
45
|
+
{ label: 'Call Logs', icon: CallLogs, to: '/call-logs' },
|
|
46
|
+
{ label: 'Email Templates', icon: EmailTemplates, to: '/email-templates' },
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
label: 'Views',
|
|
51
|
+
collapsible: true,
|
|
52
|
+
items: [
|
|
53
|
+
{ label: 'My Open Deals', icon: Link, to: '/my-open-deals' },
|
|
54
|
+
{ label: 'Partnership Deals', icon: Link, to: '/partnership-deals' },
|
|
55
|
+
{ label: 'Unassigned Deals', icon: Link, to: '/unassigned-deals' },
|
|
56
|
+
{ label: 'Enterprise Pipeline', icon: Link, to: '/enterprise-pipeline' },
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
})
|
|
61
|
+
function toggleTheme() {
|
|
62
|
+
const currentTheme = document.documentElement.getAttribute('data-theme');
|
|
63
|
+
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
64
|
+
document.documentElement.setAttribute('data-theme', newTheme);
|
|
65
|
+
}
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<Story>
|
|
70
|
+
<Variant title="Sidebar">
|
|
71
|
+
<div class="flex h-screen w-full flex-col bg-surface-white shadow">
|
|
72
|
+
<Sidebar
|
|
73
|
+
:header="crmSidebar.header"
|
|
74
|
+
:sections="crmSidebar.sections"
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
</Variant>
|
|
78
|
+
</Story>
|
|
79
|
+
</template>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="flex h-full flex-col flex-shrink-0 overflow-y-auto border-r border-outline-gray-1 bg-surface-menu-bar transition-all duration-300 ease-in-out p-2"
|
|
4
|
+
:class="isCollapsed ? 'w-12' : 'w-60'"
|
|
5
|
+
>
|
|
6
|
+
<SidebarHeader
|
|
7
|
+
v-if="props.header"
|
|
8
|
+
:isCollapsed="isCollapsed"
|
|
9
|
+
:title="props.header.title"
|
|
10
|
+
:subtitle="props.header.subtitle"
|
|
11
|
+
:menu-items="props.header.menuItems"
|
|
12
|
+
/>
|
|
13
|
+
|
|
14
|
+
<SidebarSection
|
|
15
|
+
v-for="section in props.sections"
|
|
16
|
+
:key="section.label"
|
|
17
|
+
:label="section.label"
|
|
18
|
+
:items="section.items"
|
|
19
|
+
:collapsible="section.collapsible"
|
|
20
|
+
/>
|
|
21
|
+
|
|
22
|
+
<div class="mt-auto flex flex-col gap-2">
|
|
23
|
+
<slot name="footer-items" />
|
|
24
|
+
<SidebarItem
|
|
25
|
+
:label="isCollapsed ? 'Expand' : 'Collapse'"
|
|
26
|
+
:isCollapsed="isCollapsed"
|
|
27
|
+
@click="isCollapsed = !isCollapsed"
|
|
28
|
+
>
|
|
29
|
+
<template #icon>
|
|
30
|
+
<LucidePanelRightOpen
|
|
31
|
+
class="size-4 text-ink-gray-6 duration-300 ease-in-out"
|
|
32
|
+
:class="{ 'rotate-180': isCollapsed }"
|
|
33
|
+
/>
|
|
34
|
+
</template>
|
|
35
|
+
</SidebarItem>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script setup lang="ts">
|
|
41
|
+
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
|
|
42
|
+
import { provide, ref, watchEffect } from 'vue'
|
|
43
|
+
import SidebarHeader from './SidebarHeader.vue'
|
|
44
|
+
import SidebarItem from './SidebarItem.vue'
|
|
45
|
+
import { SidebarProps } from './types'
|
|
46
|
+
|
|
47
|
+
import LucidePanelRightOpen from '~icons/lucide/panel-right-open'
|
|
48
|
+
import SidebarSection from './SidebarSection.vue'
|
|
49
|
+
|
|
50
|
+
const props = defineProps<SidebarProps>()
|
|
51
|
+
|
|
52
|
+
const isCollapsed = ref(false)
|
|
53
|
+
provide('isSidebarCollapsed', isCollapsed)
|
|
54
|
+
|
|
55
|
+
const breakpoints = useBreakpoints(breakpointsTailwind)
|
|
56
|
+
const isMobile = breakpoints.smaller('sm')
|
|
57
|
+
watchEffect(() => {
|
|
58
|
+
isCollapsed.value = isMobile.value
|
|
59
|
+
})
|
|
60
|
+
</script>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Dropdown :options="props.menuItems">
|
|
3
|
+
<template v-slot="{ open }">
|
|
4
|
+
<button
|
|
5
|
+
class="flex h-12 items-center rounded-md py-2 duration-300 ease-in-out w-[14rem]"
|
|
6
|
+
:class="
|
|
7
|
+
isCollapsed
|
|
8
|
+
? 'w-auto px-0'
|
|
9
|
+
: open
|
|
10
|
+
? 'bg-surface-white px-2 shadow-sm'
|
|
11
|
+
: 'px-2 hover:bg-surface-gray-3'
|
|
12
|
+
"
|
|
13
|
+
>
|
|
14
|
+
<slot name="logo">
|
|
15
|
+
<div class="w-8 h-8 rounded bg-surface-gray-4 flex items-center justify-center text-ink-gray-7">
|
|
16
|
+
{{ props.title.charAt(0).toUpperCase() }}
|
|
17
|
+
</div>
|
|
18
|
+
</slot>
|
|
19
|
+
<div
|
|
20
|
+
class="flex flex-1 flex-col text-left duration-300 ease-in-out truncate"
|
|
21
|
+
:class="
|
|
22
|
+
isCollapsed
|
|
23
|
+
? 'ml-0 w-0 overflow-hidden opacity-0'
|
|
24
|
+
: 'ml-2 w-auto opacity-100'
|
|
25
|
+
"
|
|
26
|
+
>
|
|
27
|
+
<div class="text-base font-medium text-ink-gray-8 leading-none">{{ props.title }}</div>
|
|
28
|
+
<div class="mt-1 text-sm text-ink-gray-6 leading-none">
|
|
29
|
+
{{ props.subtitle }}
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div
|
|
33
|
+
class="duration-300 ease-in-out"
|
|
34
|
+
:class="
|
|
35
|
+
isCollapsed
|
|
36
|
+
? 'ml-0 w-0 overflow-hidden opacity-0'
|
|
37
|
+
: 'ml-2 w-auto opacity-100'
|
|
38
|
+
"
|
|
39
|
+
>
|
|
40
|
+
<LucideChevronDown class="h-4 w-4 text-ink-gray-7"/>
|
|
41
|
+
</div>
|
|
42
|
+
</button>
|
|
43
|
+
</template>
|
|
44
|
+
</Dropdown>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
import { inject } from 'vue';
|
|
49
|
+
import LucideChevronDown from '~icons/lucide/chevron-down';
|
|
50
|
+
import { SidebarHeaderProps } from './types';
|
|
51
|
+
import Dropdown from '../Dropdown/Dropdown.vue';
|
|
52
|
+
|
|
53
|
+
const props = defineProps<SidebarHeaderProps>();
|
|
54
|
+
const isCollapsed = inject('isSidebarCollapsed', false);
|
|
55
|
+
</script>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Button
|
|
3
|
+
:label="props.label"
|
|
4
|
+
@click="handleClick"
|
|
5
|
+
class="!w-full"
|
|
6
|
+
:class="
|
|
7
|
+
props.isActive
|
|
8
|
+
? '!bg-surface-selected shadow-sm'
|
|
9
|
+
: 'hover:bg-surface-gray-2'
|
|
10
|
+
"
|
|
11
|
+
variant="ghost"
|
|
12
|
+
>
|
|
13
|
+
<template #icon>
|
|
14
|
+
<div
|
|
15
|
+
class="flex w-full items-center justify-between transition-all ease-in-out px-2 py-1"
|
|
16
|
+
>
|
|
17
|
+
<div class="flex items-center truncate">
|
|
18
|
+
<Tooltip
|
|
19
|
+
:text="props.label"
|
|
20
|
+
placement="right"
|
|
21
|
+
:disabled="!isCollapsed"
|
|
22
|
+
>
|
|
23
|
+
<span class="grid flex-shrink-0 place-items-center">
|
|
24
|
+
<slot name="icon">
|
|
25
|
+
<span
|
|
26
|
+
v-if="props.icon && typeof props.icon === 'string'"
|
|
27
|
+
class="size-4 text-ink-gray-6"
|
|
28
|
+
>
|
|
29
|
+
{{ props.icon }}
|
|
30
|
+
</span>
|
|
31
|
+
<component
|
|
32
|
+
v-else
|
|
33
|
+
:is="props.icon"
|
|
34
|
+
class="size-4 text-ink-gray-6"
|
|
35
|
+
/>
|
|
36
|
+
</slot>
|
|
37
|
+
</span>
|
|
38
|
+
</Tooltip>
|
|
39
|
+
<Tooltip
|
|
40
|
+
:text="props.label"
|
|
41
|
+
placement="right"
|
|
42
|
+
:disabled="isCollapsed"
|
|
43
|
+
:hoverDelay="1.5"
|
|
44
|
+
>
|
|
45
|
+
<span
|
|
46
|
+
class="flex-1 flex-shrink-0 truncate text-sm transition-all ease-in-out"
|
|
47
|
+
:class="
|
|
48
|
+
isCollapsed
|
|
49
|
+
? 'ml-0 w-0 overflow-hidden opacity-0'
|
|
50
|
+
: 'ml-2 w-auto opacity-100'
|
|
51
|
+
"
|
|
52
|
+
>
|
|
53
|
+
{{ props.label }}
|
|
54
|
+
</span>
|
|
55
|
+
</Tooltip>
|
|
56
|
+
</div>
|
|
57
|
+
<div
|
|
58
|
+
class="transition-all ease-in-out"
|
|
59
|
+
:class="
|
|
60
|
+
isCollapsed
|
|
61
|
+
? 'ml-0 w-0 overflow-hidden opacity-0'
|
|
62
|
+
: 'ml-auto w-auto opacity-100'
|
|
63
|
+
"
|
|
64
|
+
>
|
|
65
|
+
<slot name="suffix">
|
|
66
|
+
<span v-if="props.suffix" class="text-sm text-ink-gray-4">
|
|
67
|
+
{{ props.suffix }}
|
|
68
|
+
</span>
|
|
69
|
+
</slot>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</template>
|
|
73
|
+
</Button>
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<script setup lang="ts">
|
|
77
|
+
import { inject } from 'vue'
|
|
78
|
+
import { useRouter } from 'vue-router'
|
|
79
|
+
import Button from '../Button/Button.vue'
|
|
80
|
+
import Tooltip from '../Tooltip/Tooltip.vue'
|
|
81
|
+
import { SidebarItemProps } from './types'
|
|
82
|
+
|
|
83
|
+
const props = defineProps<SidebarItemProps>()
|
|
84
|
+
const isCollapsed = inject('isSidebarCollapsed', false)
|
|
85
|
+
|
|
86
|
+
const router = useRouter()
|
|
87
|
+
function handleClick() {
|
|
88
|
+
if (props.onClick) {
|
|
89
|
+
props.onClick()
|
|
90
|
+
} else if (props.to) {
|
|
91
|
+
router.replace(props.to)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col mt-2">
|
|
3
|
+
<div
|
|
4
|
+
v-if="props.label"
|
|
5
|
+
class="relative flex items-center gap-1 px-2 py-1.5"
|
|
6
|
+
:class="props.collapsible ? 'cursor-pointer' : ''"
|
|
7
|
+
@click="isCollapsed = !isCollapsed"
|
|
8
|
+
>
|
|
9
|
+
<h3
|
|
10
|
+
class="h-4 text-sm text-ink-gray-5 transition-all duration-300 ease-in-out"
|
|
11
|
+
:class="
|
|
12
|
+
isSidebarCollapsed
|
|
13
|
+
? 'w-0 overflow-hidden opacity-0'
|
|
14
|
+
: 'w-auto opacity-100'
|
|
15
|
+
"
|
|
16
|
+
>
|
|
17
|
+
{{ props.label }}
|
|
18
|
+
</h3>
|
|
19
|
+
<div v-if="props.collapsible">
|
|
20
|
+
<LucideChevronRight
|
|
21
|
+
v-if="!isSidebarCollapsed"
|
|
22
|
+
class="w-4 h-4 text-ink-gray-5 transition-all duration-300 ease-in-out"
|
|
23
|
+
:class="{ 'rotate-90': !isCollapsed }"
|
|
24
|
+
/>
|
|
25
|
+
</div>
|
|
26
|
+
<div
|
|
27
|
+
v-if="isSidebarCollapsed"
|
|
28
|
+
class="absolute top-0 left-0 flex h-full w-full items-center justify-center transition-all duration-300 ease-in-out"
|
|
29
|
+
:class="isSidebarCollapsed ? 'opacity-100' : 'opacity-0'"
|
|
30
|
+
>
|
|
31
|
+
<hr class="w-full border-t border-ink-gray-3" />
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<transition
|
|
35
|
+
enter-active-class="duration-300 ease-in"
|
|
36
|
+
leave-active-class="duration-300 ease-[cubic-bezier(0, 1, 0.5, 1)]"
|
|
37
|
+
enter-to-class="max-h-[200px] overflow-hidden"
|
|
38
|
+
leave-from-class="max-h-[200px] overflow-hidden"
|
|
39
|
+
enter-from-class="max-h-0 overflow-hidden"
|
|
40
|
+
leave-to-class="max-h-0 overflow-hidden"
|
|
41
|
+
>
|
|
42
|
+
<nav v-if="!isCollapsed" class="space-y-0.5">
|
|
43
|
+
<SidebarItem
|
|
44
|
+
v-for="item in props.items"
|
|
45
|
+
:key="item.label"
|
|
46
|
+
:label="item.label"
|
|
47
|
+
:icon="item.icon"
|
|
48
|
+
:suffix="item.suffix"
|
|
49
|
+
:to="item.to"
|
|
50
|
+
:isActive="item.isActive"
|
|
51
|
+
:isCollapsed="isSidebarCollapsed"
|
|
52
|
+
:onClick="item.onClick"
|
|
53
|
+
>
|
|
54
|
+
</SidebarItem>
|
|
55
|
+
</nav>
|
|
56
|
+
</transition>
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
|
59
|
+
|
|
60
|
+
<script setup lang="ts">
|
|
61
|
+
import { inject, ref } from 'vue';
|
|
62
|
+
import SidebarItem from './SidebarItem.vue';
|
|
63
|
+
import { SidebarSectionProps } from './types';
|
|
64
|
+
|
|
65
|
+
const props = defineProps<SidebarSectionProps>()
|
|
66
|
+
|
|
67
|
+
const isSidebarCollapsed = inject('isSidebarCollapsed', false)
|
|
68
|
+
const isCollapsed = ref(false)
|
|
69
|
+
</script>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { RouteLocationRaw } from 'vue-router'
|
|
2
|
+
|
|
3
|
+
export type SidebarHeaderProps = {
|
|
4
|
+
title: string
|
|
5
|
+
subtitle?: string
|
|
6
|
+
menuItems?: {
|
|
7
|
+
label: string
|
|
8
|
+
icon: any // Icon component
|
|
9
|
+
onClick?: () => void
|
|
10
|
+
}[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type SidebarItemProps = {
|
|
14
|
+
label: string
|
|
15
|
+
icon?: any // Icon component
|
|
16
|
+
suffix?: string
|
|
17
|
+
to?: RouteLocationRaw
|
|
18
|
+
isActive?: boolean
|
|
19
|
+
onClick?: () => void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type SidebarSectionProps = {
|
|
23
|
+
label?: string
|
|
24
|
+
items: SidebarItemProps[]
|
|
25
|
+
collapsible?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type SidebarProps = {
|
|
29
|
+
header?: SidebarHeaderProps
|
|
30
|
+
sections?: SidebarSectionProps[]
|
|
31
|
+
}
|
|
@@ -111,9 +111,6 @@ export default Node.create<VideoOptions>({
|
|
|
111
111
|
(editor.isEditable ? ' cursor-pointer' : '')
|
|
112
112
|
|
|
113
113
|
const video = document.createElement('video')
|
|
114
|
-
if (editor.isEditable) {
|
|
115
|
-
video.className = 'pointer-events-none'
|
|
116
|
-
}
|
|
117
114
|
video.src = node.attrs.src
|
|
118
115
|
video.setAttribute('controls', '')
|
|
119
116
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import DOMPurify from 'dompurify'
|
|
2
|
+
import { defineComponent, h, ref, Ref } from 'vue'
|
|
3
|
+
import LoadingIndicator from '../LoadingIndicator.vue'
|
|
2
4
|
import ToastComponent from './Toast.vue'
|
|
3
5
|
import type { ToastProps } from './types'
|
|
4
|
-
import LoadingIndicator from '../LoadingIndicator.vue'
|
|
5
6
|
|
|
6
7
|
interface ToastOptions
|
|
7
8
|
extends Omit<Partial<ToastProps>, 'open' | 'message' | 'title'> {
|
|
@@ -56,10 +57,14 @@ export const toast = {
|
|
|
56
57
|
const durationInMs =
|
|
57
58
|
options.duration != null ? options.duration * 1000 : 5000
|
|
58
59
|
|
|
60
|
+
const sanitizedMessage = DOMPurify.sanitize(options.message, {
|
|
61
|
+
ALLOWED_TAGS: ['a', 'em', 'strong', 'i', 'b', 'u'],
|
|
62
|
+
})
|
|
63
|
+
|
|
59
64
|
const toastItem: ToastItem = {
|
|
60
65
|
id: options.id || id,
|
|
61
66
|
open: true,
|
|
62
|
-
message:
|
|
67
|
+
message: sanitizedMessage,
|
|
63
68
|
type: options.type || 'info',
|
|
64
69
|
duration: durationInMs,
|
|
65
70
|
action: options.action,
|
package/src/index.ts
CHANGED
|
@@ -70,6 +70,7 @@ export { default as NestedPopover } from './components/ListFilter/NestedPopover.
|
|
|
70
70
|
export * from './components/CircularProgressBar'
|
|
71
71
|
export * from './components/Tree'
|
|
72
72
|
export { default as FrappeUIProvider } from './components/Provider/FrappeUIProvider.vue'
|
|
73
|
+
export { default as Sidebar } from './components/Sidebar/Sidebar.vue'
|
|
73
74
|
|
|
74
75
|
// chart components
|
|
75
76
|
export { default as AxisChart } from './components/Charts/AxisChart.vue'
|