@skewedaspect/sleekspace-ui 0.2.0-beta.1
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/LICENSE +21 -0
- package/README.md +111 -0
- package/dist/sleekspace-ui.css +12844 -0
- package/dist/sleekspace-ui.es.js +19021 -0
- package/dist/sleekspace-ui.umd.js +19040 -0
- package/docs/components/accordion.md +92 -0
- package/docs/components/alert.md +72 -0
- package/docs/components/avatar.md +69 -0
- package/docs/components/breadcrumbs.md +65 -0
- package/docs/components/button/_meta.yaml +12 -0
- package/docs/components/button/accessibility.md +16 -0
- package/docs/components/button/custom-colors.md +18 -0
- package/docs/components/button/icons.md +31 -0
- package/docs/components/button/intro.md +8 -0
- package/docs/components/button/kinds.md +25 -0
- package/docs/components/button/sizes.md +14 -0
- package/docs/components/button/states.md +12 -0
- package/docs/components/button/usage.md +23 -0
- package/docs/components/button/variants.md +14 -0
- package/docs/components/button.md +110 -0
- package/docs/components/card.md +87 -0
- package/docs/components/checkbox.md +77 -0
- package/docs/components/collapsible.md +71 -0
- package/docs/components/divider.md +62 -0
- package/docs/components/dropdown.md +88 -0
- package/docs/components/field.md +80 -0
- package/docs/components/group.md +41 -0
- package/docs/components/input.md +84 -0
- package/docs/components/listbox.md +82 -0
- package/docs/components/modal.md +101 -0
- package/docs/components/navbar.md +64 -0
- package/docs/components/number-input.md +78 -0
- package/docs/components/page.md +77 -0
- package/docs/components/pagination.md +88 -0
- package/docs/components/panel.md +74 -0
- package/docs/components/popover.md +93 -0
- package/docs/components/progress.md +76 -0
- package/docs/components/radio.md +86 -0
- package/docs/components/sidebar.md +74 -0
- package/docs/components/skeleton.md +76 -0
- package/docs/components/slider.md +94 -0
- package/docs/components/spinner.md +59 -0
- package/docs/components/switch.md +97 -0
- package/docs/components/table.md +91 -0
- package/docs/components/tabs.md +108 -0
- package/docs/components/tag.md +75 -0
- package/docs/components/tags-input.md +88 -0
- package/docs/components/textarea.md +80 -0
- package/docs/components/theme.md +65 -0
- package/docs/components/toast.md +95 -0
- package/docs/components/tooltip.md +90 -0
- package/docs/guides/custom-colors.md +84 -0
- package/docs/guides/design-tokens.md +105 -0
- package/docs/guides/getting-started.md +144 -0
- package/docs/guides/installation.md +62 -0
- package/docs/guides/theming.md +101 -0
- package/package.json +76 -0
- package/src/components/Accordion/SkAccordion.vue +133 -0
- package/src/components/Accordion/SkAccordionItem.vue +131 -0
- package/src/components/Accordion/index.ts +3 -0
- package/src/components/Accordion/types.ts +9 -0
- package/src/components/Alert/SkAlert.vue +137 -0
- package/src/components/Alert/types.ts +10 -0
- package/src/components/Avatar/SkAvatar.vue +141 -0
- package/src/components/Avatar/index.ts +8 -0
- package/src/components/Avatar/types.ts +31 -0
- package/src/components/Breadcrumbs/SkBreadcrumbItem.vue +76 -0
- package/src/components/Breadcrumbs/SkBreadcrumbSeparator.vue +38 -0
- package/src/components/Breadcrumbs/SkBreadcrumbs.vue +93 -0
- package/src/components/Breadcrumbs/index.ts +10 -0
- package/src/components/Breadcrumbs/types.ts +36 -0
- package/src/components/Button/SkButton.vue +148 -0
- package/src/components/Button/types.ts +21 -0
- package/src/components/Card/SkCard.vue +144 -0
- package/src/components/Card/types.ts +12 -0
- package/src/components/Checkbox/SkCheckbox.vue +136 -0
- package/src/components/Checkbox/index.ts +8 -0
- package/src/components/Checkbox/types.ts +10 -0
- package/src/components/Collapsible/SkCollapsible.vue +159 -0
- package/src/components/Collapsible/index.ts +2 -0
- package/src/components/Collapsible/types.ts +8 -0
- package/src/components/Divider/SkDivider.vue +63 -0
- package/src/components/Divider/types.ts +15 -0
- package/src/components/Dropdown/SkDropdown.vue +150 -0
- package/src/components/Dropdown/SkDropdownMenuItem.vue +58 -0
- package/src/components/Dropdown/SkDropdownMenuSeparator.vue +26 -0
- package/src/components/Dropdown/SkDropdownSubmenu.vue +107 -0
- package/src/components/Dropdown/index.ts +11 -0
- package/src/components/Dropdown/types.ts +11 -0
- package/src/components/Field/SkField.vue +152 -0
- package/src/components/Field/index.ts +8 -0
- package/src/components/Field/types.ts +7 -0
- package/src/components/Group/SkGroup.vue +52 -0
- package/src/components/Group/types.ts +10 -0
- package/src/components/Input/SkInput.vue +117 -0
- package/src/components/Input/index.ts +8 -0
- package/src/components/Input/types.ts +11 -0
- package/src/components/Listbox/SkListbox.vue +164 -0
- package/src/components/Listbox/SkListboxItem.vue +68 -0
- package/src/components/Listbox/SkListboxSeparator.vue +26 -0
- package/src/components/Listbox/index.ts +10 -0
- package/src/components/Listbox/types.ts +10 -0
- package/src/components/Modal/SkModal.vue +231 -0
- package/src/components/Modal/index.ts +8 -0
- package/src/components/Modal/types.ts +12 -0
- package/src/components/NavBar/SkNavBar.vue +83 -0
- package/src/components/NavBar/index.ts +8 -0
- package/src/components/NavBar/types.ts +15 -0
- package/src/components/NumberInput/SkNumberInput.vue +168 -0
- package/src/components/NumberInput/index.ts +8 -0
- package/src/components/NumberInput/types.ts +10 -0
- package/src/components/Page/SkPage.vue +94 -0
- package/src/components/Page/index.ts +8 -0
- package/src/components/Page/types.ts +21 -0
- package/src/components/Pagination/SkPagination.vue +185 -0
- package/src/components/Pagination/SkPaginationItem.vue +107 -0
- package/src/components/Pagination/index.ts +9 -0
- package/src/components/Pagination/types.ts +40 -0
- package/src/components/Panel/SkPanel.vue +96 -0
- package/src/components/Panel/types.ts +15 -0
- package/src/components/Popover/SkPopover.vue +185 -0
- package/src/components/Popover/index.ts +8 -0
- package/src/components/Popover/types.ts +11 -0
- package/src/components/Progress/SkProgress.vue +144 -0
- package/src/components/Progress/index.ts +8 -0
- package/src/components/Progress/types.ts +34 -0
- package/src/components/Radio/SkRadio.vue +110 -0
- package/src/components/Radio/SkRadioGroup.vue +92 -0
- package/src/components/Radio/index.ts +9 -0
- package/src/components/Radio/types.ts +11 -0
- package/src/components/Sidebar/README.md +405 -0
- package/src/components/Sidebar/SkSidebar.vue +88 -0
- package/src/components/Sidebar/SkSidebarItem.vue +58 -0
- package/src/components/Sidebar/SkSidebarSection.vue +40 -0
- package/src/components/Sidebar/types.ts +3 -0
- package/src/components/Skeleton/SkSkeleton.vue +171 -0
- package/src/components/Skeleton/index.ts +8 -0
- package/src/components/Skeleton/types.ts +31 -0
- package/src/components/Slider/SkSlider.vue +165 -0
- package/src/components/Slider/index.ts +8 -0
- package/src/components/Slider/types.ts +44 -0
- package/src/components/Spinner/SkSpinner.vue +105 -0
- package/src/components/Spinner/index.ts +8 -0
- package/src/components/Spinner/types.ts +28 -0
- package/src/components/Switch/SkSwitch.vue +215 -0
- package/src/components/Switch/index.ts +8 -0
- package/src/components/Switch/types.ts +12 -0
- package/src/components/Table/SkTable.vue +109 -0
- package/src/components/Table/index.ts +2 -0
- package/src/components/Table/types.ts +15 -0
- package/src/components/Tabs/README.md +331 -0
- package/src/components/Tabs/SkTab.vue +84 -0
- package/src/components/Tabs/SkTabList.vue +62 -0
- package/src/components/Tabs/SkTabPanel.vue +47 -0
- package/src/components/Tabs/SkTabPanels.vue +23 -0
- package/src/components/Tabs/SkTabs.vue +124 -0
- package/src/components/Tabs/types.ts +21 -0
- package/src/components/Tag/SkTag.vue +129 -0
- package/src/components/Tag/types.ts +15 -0
- package/src/components/TagsInput/SkTagsInput.vue +184 -0
- package/src/components/TagsInput/index.ts +8 -0
- package/src/components/TagsInput/types.ts +10 -0
- package/src/components/Textarea/SkTextarea.vue +117 -0
- package/src/components/Textarea/index.ts +8 -0
- package/src/components/Textarea/types.ts +10 -0
- package/src/components/Theme/SkTheme.vue +47 -0
- package/src/components/Theme/types.ts +17 -0
- package/src/components/Theme/useTheme.ts +131 -0
- package/src/components/Toast/SkToast.vue +156 -0
- package/src/components/Toast/SkToastProvider.vue +180 -0
- package/src/components/Toast/index.ts +15 -0
- package/src/components/Toast/types.ts +63 -0
- package/src/components/Toast/useToast.ts +78 -0
- package/src/components/Tooltip/SkTooltip.vue +162 -0
- package/src/components/Tooltip/SkTooltipProvider.vue +114 -0
- package/src/components/Tooltip/index.ts +9 -0
- package/src/components/Tooltip/types.ts +13 -0
- package/src/composables/useCustomColors.test.ts +505 -0
- package/src/composables/useCustomColors.ts +124 -0
- package/src/composables/usePortalContext.test.ts +402 -0
- package/src/composables/usePortalContext.ts +95 -0
- package/src/global.d.ts +76 -0
- package/src/index.ts +259 -0
- package/src/styles/_scrollbar.scss +100 -0
- package/src/styles/base/_fonts.scss +105 -0
- package/src/styles/base/_global.scss +47 -0
- package/src/styles/base/_index.scss +24 -0
- package/src/styles/base/_reset.scss +11 -0
- package/src/styles/base/_typography.scss +178 -0
- package/src/styles/components/_accordion.scss +250 -0
- package/src/styles/components/_alert.scss +239 -0
- package/src/styles/components/_avatar.scss +133 -0
- package/src/styles/components/_breadcrumbs.scss +137 -0
- package/src/styles/components/_button.scss +731 -0
- package/src/styles/components/_card.scss +141 -0
- package/src/styles/components/_checkbox.scss +232 -0
- package/src/styles/components/_collapsible.scss +158 -0
- package/src/styles/components/_divider.scss +121 -0
- package/src/styles/components/_field.scss +87 -0
- package/src/styles/components/_group.scss +138 -0
- package/src/styles/components/_index.scss +46 -0
- package/src/styles/components/_input.scss +205 -0
- package/src/styles/components/_listbox.scss +453 -0
- package/src/styles/components/_menu.scss +216 -0
- package/src/styles/components/_modal.scss +329 -0
- package/src/styles/components/_navbar.scss +258 -0
- package/src/styles/components/_number-input.scss +352 -0
- package/src/styles/components/_page.scss +98 -0
- package/src/styles/components/_pagination.scss +411 -0
- package/src/styles/components/_panel.scss +281 -0
- package/src/styles/components/_popover.scss +258 -0
- package/src/styles/components/_progress.scss +280 -0
- package/src/styles/components/_radio.scss +255 -0
- package/src/styles/components/_sidebar.scss +92 -0
- package/src/styles/components/_skeleton.scss +138 -0
- package/src/styles/components/_slider.scss +262 -0
- package/src/styles/components/_spinner.scss +331 -0
- package/src/styles/components/_switch.scss +370 -0
- package/src/styles/components/_table.scss +405 -0
- package/src/styles/components/_tabs.scss +486 -0
- package/src/styles/components/_tag.scss +425 -0
- package/src/styles/components/_tags-input.scss +279 -0
- package/src/styles/components/_textarea.scss +208 -0
- package/src/styles/components/_toast.scss +331 -0
- package/src/styles/components/_tooltip.scss +206 -0
- package/src/styles/fonts/Titillium_Web/OFL.txt +93 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-Black.ttf +0 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-Bold.ttf +0 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-BoldItalic.ttf +0 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-ExtraLight.ttf +0 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-ExtraLightItalic.ttf +0 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-Italic.ttf +0 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-Light.ttf +0 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-LightItalic.ttf +0 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-Regular.ttf +0 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-SemiBold.ttf +0 -0
- package/src/styles/fonts/Titillium_Web/TitilliumWeb-SemiBoldItalic.ttf +0 -0
- package/src/styles/index.scss +17 -0
- package/src/styles/mixins/_cut-border.scss +254 -0
- package/src/styles/mixins/_index.scss +7 -0
- package/src/styles/theme/_variables.scss +42 -0
- package/src/styles/themes/README.md +127 -0
- package/src/styles/themes/_colorful.scss +58 -0
- package/src/styles/themes/_greyscale.scss +58 -0
- package/src/styles/themes/index.scss +9 -0
- package/src/styles/tokens/README.md +268 -0
- package/src/styles/tokens/_foundation-borders.scss +26 -0
- package/src/styles/tokens/_foundation-colors.scss +169 -0
- package/src/styles/tokens/_foundation-glow.scss +36 -0
- package/src/styles/tokens/_foundation-radius.scss +53 -0
- package/src/styles/tokens/_foundation-scrollbar.scss +31 -0
- package/src/styles/tokens/_foundation-shadows.scss +37 -0
- package/src/styles/tokens/_foundation-space.scss +36 -0
- package/src/styles/tokens/_foundation-transitions.scss +37 -0
- package/src/styles/tokens/_foundation-typography.scss +58 -0
- package/src/styles/tokens/_semantic-color-kinds.scss +112 -0
- package/src/styles/tokens/_semantic-colors.scss +10 -0
- package/src/styles/tokens/_semantic-interactive.scss +29 -0
- package/src/styles/tokens/_semantic-scrollbar.scss +48 -0
- package/src/styles/tokens/_semantic-surfaces.scss +36 -0
- package/src/styles/tokens/index.scss +38 -0
- package/src/styles/tokens.scss +268 -0
- package/src/styles/utilities/_index.scss +9 -0
- package/src/styles/utilities/_typography.scss +121 -0
- package/src/types.ts +50 -0
- package/web-types.json +3524 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ToastRoot
|
|
3
|
+
:class="classes"
|
|
4
|
+
:duration="duration"
|
|
5
|
+
@update:open="onOpenChange"
|
|
6
|
+
>
|
|
7
|
+
<!-- Icon -->
|
|
8
|
+
<div class="sk-toast-icon">
|
|
9
|
+
<svg
|
|
10
|
+
v-if="kind === 'info'"
|
|
11
|
+
viewBox="0 0 24 24"
|
|
12
|
+
fill="none"
|
|
13
|
+
stroke="currentColor"
|
|
14
|
+
stroke-width="2"
|
|
15
|
+
>
|
|
16
|
+
<circle cx="12" cy="12" r="10" />
|
|
17
|
+
<line x1="12" y1="16" x2="12" y2="12" />
|
|
18
|
+
<circle cx="12" cy="8" r="0.5" fill="currentColor" />
|
|
19
|
+
</svg>
|
|
20
|
+
<svg
|
|
21
|
+
v-else-if="kind === 'success'"
|
|
22
|
+
viewBox="0 0 24 24"
|
|
23
|
+
fill="none"
|
|
24
|
+
stroke="currentColor"
|
|
25
|
+
stroke-width="2"
|
|
26
|
+
>
|
|
27
|
+
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
28
|
+
<polyline points="22 4 12 14.01 9 11.01" />
|
|
29
|
+
</svg>
|
|
30
|
+
<svg
|
|
31
|
+
v-else-if="kind === 'warning'"
|
|
32
|
+
viewBox="0 0 24 24"
|
|
33
|
+
fill="none"
|
|
34
|
+
stroke="currentColor"
|
|
35
|
+
stroke-width="2"
|
|
36
|
+
>
|
|
37
|
+
<path
|
|
38
|
+
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
|
|
39
|
+
/>
|
|
40
|
+
<line x1="12" y1="9" x2="12" y2="13" />
|
|
41
|
+
<circle cx="12" cy="17" r="0.5" fill="currentColor" />
|
|
42
|
+
</svg>
|
|
43
|
+
<svg
|
|
44
|
+
v-else-if="kind === 'danger'"
|
|
45
|
+
viewBox="0 0 24 24"
|
|
46
|
+
fill="none"
|
|
47
|
+
stroke="currentColor"
|
|
48
|
+
stroke-width="2"
|
|
49
|
+
>
|
|
50
|
+
<circle cx="12" cy="12" r="10" />
|
|
51
|
+
<line x1="15" y1="9" x2="9" y2="15" />
|
|
52
|
+
<line x1="9" y1="9" x2="15" y2="15" />
|
|
53
|
+
</svg>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<!-- Content -->
|
|
57
|
+
<div class="sk-toast-content">
|
|
58
|
+
<ToastTitle v-if="title" class="sk-toast-title">
|
|
59
|
+
{{ title }}
|
|
60
|
+
</ToastTitle>
|
|
61
|
+
<ToastDescription class="sk-toast-description">
|
|
62
|
+
{{ message }}
|
|
63
|
+
</ToastDescription>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<!-- Close button -->
|
|
67
|
+
<ToastClose v-if="closable" class="sk-toast-close" aria-label="Close">
|
|
68
|
+
<svg
|
|
69
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
70
|
+
width="16"
|
|
71
|
+
height="16"
|
|
72
|
+
viewBox="0 0 24 24"
|
|
73
|
+
fill="none"
|
|
74
|
+
stroke="currentColor"
|
|
75
|
+
stroke-width="2"
|
|
76
|
+
stroke-linecap="round"
|
|
77
|
+
stroke-linejoin="round"
|
|
78
|
+
>
|
|
79
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
80
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
81
|
+
</svg>
|
|
82
|
+
</ToastClose>
|
|
83
|
+
</ToastRoot>
|
|
84
|
+
</template>
|
|
85
|
+
|
|
86
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
87
|
+
|
|
88
|
+
<style lang="scss" scoped>
|
|
89
|
+
// Toast styles are implemented in /src/styles/components/_toast.scss
|
|
90
|
+
</style>
|
|
91
|
+
|
|
92
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
93
|
+
|
|
94
|
+
<script setup lang="ts">
|
|
95
|
+
/**
|
|
96
|
+
* @component
|
|
97
|
+
* Internal toast component used by SkToastProvider.
|
|
98
|
+
* Not exported directly - use useToast() to create toasts.
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
import { computed } from 'vue';
|
|
102
|
+
import {
|
|
103
|
+
ToastClose,
|
|
104
|
+
ToastDescription,
|
|
105
|
+
ToastRoot,
|
|
106
|
+
ToastTitle,
|
|
107
|
+
} from 'reka-ui';
|
|
108
|
+
|
|
109
|
+
// Types
|
|
110
|
+
import type { SkToastKind } from './types';
|
|
111
|
+
|
|
112
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
interface SkToastProps
|
|
115
|
+
{
|
|
116
|
+
/** Toast ID for tracking */
|
|
117
|
+
id : string;
|
|
118
|
+
/** Semantic kind */
|
|
119
|
+
kind : SkToastKind;
|
|
120
|
+
/** Optional title */
|
|
121
|
+
title ?: string;
|
|
122
|
+
/** Toast message */
|
|
123
|
+
message : string;
|
|
124
|
+
/** Duration in ms (0 = no auto-dismiss) */
|
|
125
|
+
duration ?: number;
|
|
126
|
+
/** Whether to show close button */
|
|
127
|
+
closable : boolean;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
const props = defineProps<SkToastProps>();
|
|
133
|
+
|
|
134
|
+
const emit = defineEmits<{
|
|
135
|
+
dismiss : [ id : string ];
|
|
136
|
+
}>();
|
|
137
|
+
|
|
138
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
const classes = computed(() => ({
|
|
141
|
+
'sk-toast': true,
|
|
142
|
+
[`sk-${ props.kind }`]: true,
|
|
143
|
+
}));
|
|
144
|
+
|
|
145
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
function onOpenChange(open : boolean) : void
|
|
148
|
+
{
|
|
149
|
+
if(!open)
|
|
150
|
+
{
|
|
151
|
+
emit('dismiss', props.id);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ToastProvider
|
|
3
|
+
:duration="duration"
|
|
4
|
+
:swipe-direction="swipeDirection"
|
|
5
|
+
:swipe-threshold="swipeThreshold"
|
|
6
|
+
>
|
|
7
|
+
<slot />
|
|
8
|
+
<ToastViewport
|
|
9
|
+
:class="viewportClasses"
|
|
10
|
+
:data-scheme="theme"
|
|
11
|
+
:hotkey="hotkey"
|
|
12
|
+
>
|
|
13
|
+
<SkToast
|
|
14
|
+
v-for="toast in toasts"
|
|
15
|
+
:id="toast.id"
|
|
16
|
+
:key="toast.id"
|
|
17
|
+
:kind="toast.kind"
|
|
18
|
+
:title="toast.title"
|
|
19
|
+
:message="toast.message"
|
|
20
|
+
:duration="toast.duration"
|
|
21
|
+
:closable="toast.closable"
|
|
22
|
+
@dismiss="dismissToast"
|
|
23
|
+
/>
|
|
24
|
+
</ToastViewport>
|
|
25
|
+
</ToastProvider>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
29
|
+
|
|
30
|
+
<style lang="scss" scoped>
|
|
31
|
+
// Toast viewport styles are implemented in /src/styles/components/_toast.scss
|
|
32
|
+
</style>
|
|
33
|
+
|
|
34
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
35
|
+
|
|
36
|
+
<script setup lang="ts">
|
|
37
|
+
/**
|
|
38
|
+
* @component
|
|
39
|
+
* Toast provider and viewport combined.
|
|
40
|
+
*
|
|
41
|
+
* Wrap your app (or a section) with this component to enable toasts.
|
|
42
|
+
* Use the useToast() composable to show toasts from anywhere in the tree.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import { computed, provide, ref } from 'vue';
|
|
46
|
+
import { ToastProvider, ToastViewport } from 'reka-ui';
|
|
47
|
+
|
|
48
|
+
// Types
|
|
49
|
+
import type { SkToastPosition, SkToastSwipeDirection, ToastAPI, ToastOptions, ToastState } from './types';
|
|
50
|
+
|
|
51
|
+
// Components
|
|
52
|
+
import SkToast from './SkToast.vue';
|
|
53
|
+
|
|
54
|
+
// Composables
|
|
55
|
+
import { usePortalContext } from '@/composables/usePortalContext';
|
|
56
|
+
import { TOAST_INJECTION_KEY } from './useToast';
|
|
57
|
+
|
|
58
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Toast provider that manages and displays toast notifications.
|
|
62
|
+
*
|
|
63
|
+
* Wrap your app with this component to enable toasts. Use useToast() composable
|
|
64
|
+
* to show notifications from anywhere in the tree.
|
|
65
|
+
*
|
|
66
|
+
* @see useToast - Composable for showing toasts
|
|
67
|
+
*/
|
|
68
|
+
export interface SkToastProviderComponentProps
|
|
69
|
+
{
|
|
70
|
+
/**
|
|
71
|
+
* Preset position for the viewport. If omitted, position via CSS/classes.
|
|
72
|
+
*/
|
|
73
|
+
position ?: SkToastPosition;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Default duration (ms) before auto-dismiss.
|
|
77
|
+
* @default 5000
|
|
78
|
+
*/
|
|
79
|
+
duration ?: number;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Direction to swipe to dismiss.
|
|
83
|
+
* @default 'right'
|
|
84
|
+
*/
|
|
85
|
+
swipeDirection ?: SkToastSwipeDirection;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Distance (px) to swipe before dismissing.
|
|
89
|
+
* @default 50
|
|
90
|
+
*/
|
|
91
|
+
swipeThreshold ?: number;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Keyboard shortcut to focus toasts.
|
|
95
|
+
* @default ['F8']
|
|
96
|
+
*/
|
|
97
|
+
hotkey ?: string[];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
const props = withDefaults(defineProps<SkToastProviderComponentProps>(), {
|
|
103
|
+
position: undefined,
|
|
104
|
+
duration: 5000,
|
|
105
|
+
swipeDirection: 'right',
|
|
106
|
+
swipeThreshold: 50,
|
|
107
|
+
hotkey: () => [ 'F8' ],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
// Handle portal context (theme injection/re-provision for nested portal components)
|
|
113
|
+
const { theme } = usePortalContext();
|
|
114
|
+
|
|
115
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
116
|
+
// Toast State Management
|
|
117
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
const toasts = ref<ToastState[]>([]);
|
|
120
|
+
let toastCounter = 0;
|
|
121
|
+
|
|
122
|
+
function generateId() : string
|
|
123
|
+
{
|
|
124
|
+
return `toast-${ ++toastCounter }-${ Date.now() }`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function addToast(options : ToastOptions) : string
|
|
128
|
+
{
|
|
129
|
+
const id = options.id ?? generateId();
|
|
130
|
+
|
|
131
|
+
const toast : ToastState = {
|
|
132
|
+
id,
|
|
133
|
+
kind: options.kind ?? 'info',
|
|
134
|
+
title: options.title ?? '',
|
|
135
|
+
message: options.message,
|
|
136
|
+
duration: options.duration,
|
|
137
|
+
closable: options.closable ?? true,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
toasts.value.push(toast);
|
|
141
|
+
return id;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function dismissToast(id : string) : void
|
|
145
|
+
{
|
|
146
|
+
const index = toasts.value.findIndex((item) => item.id === id);
|
|
147
|
+
if(index !== -1)
|
|
148
|
+
{
|
|
149
|
+
toasts.value.splice(index, 1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function dismissAllToasts() : void
|
|
154
|
+
{
|
|
155
|
+
toasts.value = [];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
159
|
+
// Provide Toast API
|
|
160
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
const toastAPI : ToastAPI = {
|
|
163
|
+
add: addToast,
|
|
164
|
+
dismiss: dismissToast,
|
|
165
|
+
dismissAll: dismissAllToasts,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
provide(TOAST_INJECTION_KEY, toastAPI);
|
|
169
|
+
|
|
170
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
171
|
+
// Viewport Classes
|
|
172
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
const viewportClasses = computed(() => ({
|
|
175
|
+
'sk-toast-viewport': true,
|
|
176
|
+
[`sk-${ props.position }`]: props.position !== undefined,
|
|
177
|
+
}));
|
|
178
|
+
</script>
|
|
179
|
+
|
|
180
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
// Toast Component Exports
|
|
3
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
export { default as SkToastProvider } from './SkToastProvider.vue';
|
|
6
|
+
export { useToast } from './useToast';
|
|
7
|
+
export type {
|
|
8
|
+
SkToastKind,
|
|
9
|
+
SkToastPosition,
|
|
10
|
+
SkToastSwipeDirection,
|
|
11
|
+
ToastAPI,
|
|
12
|
+
ToastOptions,
|
|
13
|
+
} from './types';
|
|
14
|
+
|
|
15
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
// Toast Types
|
|
3
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
/** Toast semantic kinds (subset of ComponentKind for notifications) */
|
|
6
|
+
export type SkToastKind = 'info' | 'success' | 'warning' | 'danger';
|
|
7
|
+
|
|
8
|
+
/** Toast viewport position presets */
|
|
9
|
+
export type SkToastPosition
|
|
10
|
+
= 'top-left'
|
|
11
|
+
| 'top-center'
|
|
12
|
+
| 'top-right'
|
|
13
|
+
| 'bottom-left'
|
|
14
|
+
| 'bottom-center'
|
|
15
|
+
| 'bottom-right';
|
|
16
|
+
|
|
17
|
+
/** Swipe direction for dismissing toasts */
|
|
18
|
+
export type SkToastSwipeDirection = 'right' | 'left' | 'up' | 'down';
|
|
19
|
+
|
|
20
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
21
|
+
// Toast Options (for useToast composable)
|
|
22
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
/** Options for creating a toast */
|
|
25
|
+
export interface ToastOptions
|
|
26
|
+
{
|
|
27
|
+
/** Unique ID for the toast. Auto-generated if not provided */
|
|
28
|
+
id ?: string;
|
|
29
|
+
/** Semantic kind (default: 'info') */
|
|
30
|
+
kind ?: SkToastKind;
|
|
31
|
+
/** Optional heading text */
|
|
32
|
+
title ?: string;
|
|
33
|
+
/** Toast message content (required) */
|
|
34
|
+
message : string;
|
|
35
|
+
/** Duration in ms before auto-dismiss. Use 0 for no auto-dismiss */
|
|
36
|
+
duration ?: number;
|
|
37
|
+
/** Whether to show close button (default: true) */
|
|
38
|
+
closable ?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Internal toast state */
|
|
42
|
+
export interface ToastState extends Required<Omit<ToastOptions, 'duration'>>
|
|
43
|
+
{
|
|
44
|
+
/** Duration can be undefined to use provider default */
|
|
45
|
+
duration ?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
49
|
+
// Toast API (returned by useToast)
|
|
50
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
/** Toast management API */
|
|
53
|
+
export interface ToastAPI
|
|
54
|
+
{
|
|
55
|
+
/** Add a new toast and return its ID */
|
|
56
|
+
add : (options : ToastOptions) => string;
|
|
57
|
+
/** Dismiss a specific toast by ID */
|
|
58
|
+
dismiss : (id : string) => void;
|
|
59
|
+
/** Dismiss all active toasts */
|
|
60
|
+
dismissAll : () => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
// Toast Composable
|
|
3
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import { inject } from 'vue';
|
|
6
|
+
|
|
7
|
+
// Types
|
|
8
|
+
import type { ToastAPI, ToastOptions } from './types';
|
|
9
|
+
|
|
10
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
11
|
+
// Injection Key
|
|
12
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
export const TOAST_INJECTION_KEY = Symbol.for('sk-toast-provider');
|
|
15
|
+
|
|
16
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
17
|
+
// useToast Composable
|
|
18
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get the toast API for showing notifications.
|
|
22
|
+
*
|
|
23
|
+
* Must be used within a component tree that contains SkToastProvider.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import { useToast } from '@skewedaspect/sleekspace-ui';
|
|
28
|
+
*
|
|
29
|
+
* const toast = useToast();
|
|
30
|
+
*
|
|
31
|
+
* // Show a success toast
|
|
32
|
+
* toast.add({
|
|
33
|
+
* kind: 'success',
|
|
34
|
+
* title: 'Saved!',
|
|
35
|
+
* message: 'Your changes have been saved.'
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // Show an error toast that doesn't auto-dismiss
|
|
39
|
+
* toast.add({
|
|
40
|
+
* kind: 'danger',
|
|
41
|
+
* title: 'Error',
|
|
42
|
+
* message: 'Something went wrong.',
|
|
43
|
+
* duration: 0
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* // Dismiss all toasts
|
|
47
|
+
* toast.dismissAll();
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function useToast() : ToastAPI
|
|
51
|
+
{
|
|
52
|
+
const api = inject<ToastAPI>(TOAST_INJECTION_KEY);
|
|
53
|
+
|
|
54
|
+
if(!api)
|
|
55
|
+
{
|
|
56
|
+
// Return a no-op API with warnings
|
|
57
|
+
console.warn('[SkToast] useToast() must be used within SkToastProvider');
|
|
58
|
+
return {
|
|
59
|
+
add: (_options : ToastOptions) =>
|
|
60
|
+
{
|
|
61
|
+
console.warn('[SkToast] Cannot add toast - no provider found');
|
|
62
|
+
return '';
|
|
63
|
+
},
|
|
64
|
+
dismiss: () =>
|
|
65
|
+
{
|
|
66
|
+
console.warn('[SkToast] Cannot dismiss toast - no provider found');
|
|
67
|
+
},
|
|
68
|
+
dismissAll: () =>
|
|
69
|
+
{
|
|
70
|
+
console.warn('[SkToast] Cannot dismiss toasts - no provider found');
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return api;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- When inside SkTooltipProvider, skip creating our own provider -->
|
|
3
|
+
<TooltipProvider v-if="!hasProvider" :delay-duration="delayDuration">
|
|
4
|
+
<TooltipRoot>
|
|
5
|
+
<TooltipTrigger as-child>
|
|
6
|
+
<slot name="trigger" />
|
|
7
|
+
</TooltipTrigger>
|
|
8
|
+
<TooltipPortal>
|
|
9
|
+
<TooltipContent
|
|
10
|
+
:class="contentClasses"
|
|
11
|
+
:style="customColorStyles"
|
|
12
|
+
:data-scheme="theme"
|
|
13
|
+
:side="side"
|
|
14
|
+
:align="align"
|
|
15
|
+
:side-offset="sideOffset"
|
|
16
|
+
>
|
|
17
|
+
<slot />
|
|
18
|
+
<TooltipArrow v-if="showArrow" class="sk-tooltip-arrow" />
|
|
19
|
+
</TooltipContent>
|
|
20
|
+
</TooltipPortal>
|
|
21
|
+
</TooltipRoot>
|
|
22
|
+
</TooltipProvider>
|
|
23
|
+
|
|
24
|
+
<!-- When inside SkTooltipProvider, use shared provider context -->
|
|
25
|
+
<TooltipRoot v-else>
|
|
26
|
+
<TooltipTrigger as-child>
|
|
27
|
+
<slot name="trigger" />
|
|
28
|
+
</TooltipTrigger>
|
|
29
|
+
<TooltipPortal>
|
|
30
|
+
<TooltipContent
|
|
31
|
+
:class="contentClasses"
|
|
32
|
+
:style="customColorStyles"
|
|
33
|
+
:data-scheme="theme"
|
|
34
|
+
:side="side"
|
|
35
|
+
:align="align"
|
|
36
|
+
:side-offset="sideOffset"
|
|
37
|
+
>
|
|
38
|
+
<slot />
|
|
39
|
+
<TooltipArrow v-if="showArrow" class="sk-tooltip-arrow" />
|
|
40
|
+
</TooltipContent>
|
|
41
|
+
</TooltipPortal>
|
|
42
|
+
</TooltipRoot>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
46
|
+
|
|
47
|
+
<style lang="scss" scoped>
|
|
48
|
+
// Tooltip styles are implemented in /src/styles/components/_tooltip.scss
|
|
49
|
+
</style>
|
|
50
|
+
|
|
51
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
52
|
+
|
|
53
|
+
<script setup lang="ts">
|
|
54
|
+
/**
|
|
55
|
+
* @component
|
|
56
|
+
* Tooltip component for displaying hover hints
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
import { computed, inject, toRef } from 'vue';
|
|
60
|
+
import {
|
|
61
|
+
TooltipArrow,
|
|
62
|
+
TooltipContent,
|
|
63
|
+
TooltipPortal,
|
|
64
|
+
TooltipProvider,
|
|
65
|
+
TooltipRoot,
|
|
66
|
+
TooltipTrigger,
|
|
67
|
+
} from 'reka-ui';
|
|
68
|
+
|
|
69
|
+
// Types
|
|
70
|
+
import type { ComponentCustomColors, ComponentKind } from '@/types';
|
|
71
|
+
import type { SkTooltipSide } from './types';
|
|
72
|
+
|
|
73
|
+
// Composables
|
|
74
|
+
import { useCustomColors } from '@/composables/useCustomColors';
|
|
75
|
+
import { usePortalContext } from '@/composables/usePortalContext';
|
|
76
|
+
|
|
77
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Tooltip component for displaying hover hints.
|
|
81
|
+
*
|
|
82
|
+
* Works in two modes:
|
|
83
|
+
* - **Standalone**: Each tooltip manages its own timing (default)
|
|
84
|
+
* - **With Provider**: Wrap tooltips in SkTooltipProvider for coordinated behavior
|
|
85
|
+
*
|
|
86
|
+
* The provider enables "skip delay" - moving quickly between tooltips shows them instantly.
|
|
87
|
+
*
|
|
88
|
+
* @example Standalone (works out of the box)
|
|
89
|
+
* ```vue
|
|
90
|
+
* <SkTooltip>
|
|
91
|
+
* <template #trigger><SkButton>Hover me</SkButton></template>
|
|
92
|
+
* Tooltip content
|
|
93
|
+
* </SkTooltip>
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @example With provider (coordinated tooltips)
|
|
97
|
+
* ```vue
|
|
98
|
+
* <SkTooltipProvider :delay-duration="300">
|
|
99
|
+
* <SkTooltip>...</SkTooltip>
|
|
100
|
+
* <SkTooltip>...</SkTooltip>
|
|
101
|
+
* </SkTooltipProvider>
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export interface SkTooltipComponentProps extends ComponentCustomColors
|
|
105
|
+
{
|
|
106
|
+
/** Semantic color kind */
|
|
107
|
+
kind ?: ComponentKind;
|
|
108
|
+
/** Visual variant */
|
|
109
|
+
variant ?: 'solid' | 'outline';
|
|
110
|
+
/** Which side of the trigger to show the tooltip */
|
|
111
|
+
side ?: SkTooltipSide;
|
|
112
|
+
/** Alignment along the side */
|
|
113
|
+
align ?: 'start' | 'center' | 'end';
|
|
114
|
+
/** Offset from the trigger in pixels */
|
|
115
|
+
sideOffset ?: number;
|
|
116
|
+
/**
|
|
117
|
+
* Delay before showing tooltip (ms). Only used in standalone mode.
|
|
118
|
+
* When inside SkTooltipProvider, the provider's delayDuration is used instead.
|
|
119
|
+
*/
|
|
120
|
+
delayDuration ?: number;
|
|
121
|
+
/** Whether to show the arrow pointing to the trigger */
|
|
122
|
+
showArrow ?: boolean;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
const props = withDefaults(defineProps<SkTooltipComponentProps>(), {
|
|
128
|
+
kind: 'neutral',
|
|
129
|
+
variant: 'solid',
|
|
130
|
+
side: 'top',
|
|
131
|
+
align: 'center',
|
|
132
|
+
sideOffset: 5,
|
|
133
|
+
delayDuration: 400,
|
|
134
|
+
showArrow: true,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
// Check if we're inside an SkTooltipProvider
|
|
140
|
+
const hasProvider = inject('sk-tooltip-provider', false);
|
|
141
|
+
|
|
142
|
+
// Handle portal context (theme injection/re-provision for nested portal components)
|
|
143
|
+
const { theme } = usePortalContext();
|
|
144
|
+
|
|
145
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
const contentClasses = computed(() => ({
|
|
148
|
+
'sk-tooltip-content': true,
|
|
149
|
+
[`sk-${ props.kind }`]: true,
|
|
150
|
+
[`sk-${ props.variant }`]: true,
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
const customColorStyles = useCustomColors(
|
|
156
|
+
'tooltip',
|
|
157
|
+
toRef(() => props.baseColor),
|
|
158
|
+
toRef(() => props.textColor)
|
|
159
|
+
);
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|