sv5ui 2.2.0 → 2.3.0

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 (58) hide show
  1. package/dist/components/Error/Error.svelte +89 -0
  2. package/dist/components/Error/Error.svelte.d.ts +5 -0
  3. package/dist/components/Error/error.types.d.ts +87 -0
  4. package/dist/components/Error/error.types.js +1 -0
  5. package/dist/components/Error/error.variants.d.ts +66 -0
  6. package/dist/components/Error/error.variants.js +16 -0
  7. package/dist/components/Error/index.d.ts +2 -0
  8. package/dist/components/Error/index.js +1 -0
  9. package/dist/components/Footer/Footer.svelte +71 -0
  10. package/dist/components/Footer/Footer.svelte.d.ts +5 -0
  11. package/dist/components/Footer/FooterColumns.svelte +110 -0
  12. package/dist/components/Footer/FooterColumns.svelte.d.ts +5 -0
  13. package/dist/components/Footer/footer-columns.types.d.ts +73 -0
  14. package/dist/components/Footer/footer-columns.types.js +1 -0
  15. package/dist/components/Footer/footer-columns.variants.d.ts +108 -0
  16. package/dist/components/Footer/footer-columns.variants.js +23 -0
  17. package/dist/components/Footer/footer.types.d.ts +46 -0
  18. package/dist/components/Footer/footer.types.js +1 -0
  19. package/dist/components/Footer/footer.variants.d.ts +66 -0
  20. package/dist/components/Footer/footer.variants.js +16 -0
  21. package/dist/components/Footer/index.d.ts +4 -0
  22. package/dist/components/Footer/index.js +2 -0
  23. package/dist/components/Header/Header.svelte +177 -0
  24. package/dist/components/Header/Header.svelte.d.ts +5 -0
  25. package/dist/components/Header/header.types.d.ts +124 -0
  26. package/dist/components/Header/header.types.js +1 -0
  27. package/dist/components/Header/header.variants.d.ts +108 -0
  28. package/dist/components/Header/header.variants.js +32 -0
  29. package/dist/components/Header/index.d.ts +2 -0
  30. package/dist/components/Header/index.js +1 -0
  31. package/dist/components/Main/Main.svelte +20 -0
  32. package/dist/components/Main/Main.svelte.d.ts +5 -0
  33. package/dist/components/Main/index.d.ts +2 -0
  34. package/dist/components/Main/index.js +1 -0
  35. package/dist/components/Main/main.types.d.ts +27 -0
  36. package/dist/components/Main/main.types.js +1 -0
  37. package/dist/components/Main/main.variants.d.ts +30 -0
  38. package/dist/components/Main/main.variants.js +10 -0
  39. package/dist/components/Tour/Tour.svelte +459 -0
  40. package/dist/components/Tour/Tour.svelte.d.ts +5 -0
  41. package/dist/components/Tour/index.d.ts +2 -0
  42. package/dist/components/Tour/index.js +1 -0
  43. package/dist/components/Tour/tour.types.d.ts +457 -0
  44. package/dist/components/Tour/tour.types.js +1 -0
  45. package/dist/components/Tour/tour.variants.d.ts +208 -0
  46. package/dist/components/Tour/tour.variants.js +56 -0
  47. package/dist/config.d.ts +2 -0
  48. package/dist/config.js +2 -0
  49. package/dist/hooks/index.d.ts +1 -0
  50. package/dist/hooks/index.js +1 -0
  51. package/dist/hooks/useTour/index.d.ts +1 -0
  52. package/dist/hooks/useTour/index.js +1 -0
  53. package/dist/hooks/useTour/useTour.svelte.d.ts +29 -0
  54. package/dist/hooks/useTour/useTour.svelte.js +194 -0
  55. package/dist/index.d.ts +5 -0
  56. package/dist/index.js +5 -0
  57. package/dist/theme.css +3 -0
  58. package/package.json +1 -1
@@ -0,0 +1,56 @@
1
+ import { tv } from 'tailwind-variants';
2
+ export const tourVariants = tv({
3
+ slots: {
4
+ overlay: 'fixed inset-0 z-[var(--sv5ui-tour-overlay-z,100)]',
5
+ spotlight: [
6
+ 'fixed top-0 left-0 z-[var(--sv5ui-tour-overlay-z,100)] pointer-events-none',
7
+ 'shadow-[0_0_0_9999px_rgba(0,0,0,0.5)]'
8
+ ],
9
+ panel: [
10
+ 'z-[var(--sv5ui-tour-panel-z,101)] flex flex-col',
11
+ 'bg-surface-container-lowest text-on-surface ring ring-surface-container-highest',
12
+ 'rounded-xl shadow-xl focus:outline-none',
13
+ 'w-[var(--sv5ui-tour-w)] max-w-[calc(100vw-2rem)]'
14
+ ],
15
+ arrow: 'bg-surface-container-lowest border-surface-container-highest',
16
+ header: 'relative flex items-start justify-between gap-2 p-4 pb-0',
17
+ title: 'font-semibold text-sm leading-snug',
18
+ description: 'text-sm text-on-surface-variant',
19
+ body: 'px-4 py-3 text-sm text-on-surface-variant',
20
+ footer: 'flex items-center justify-between gap-3 p-4 pt-0',
21
+ progress: 'flex items-center gap-1.5',
22
+ progressDot: 'size-1.5 rounded-full bg-on-surface-variant/30 transition-colors',
23
+ nav: 'flex items-center gap-2 ml-auto',
24
+ prevButton: '',
25
+ nextButton: '',
26
+ skipButton: 'text-on-surface-variant',
27
+ closeButton: 'shrink-0 -mt-1 -mr-1'
28
+ },
29
+ variants: {
30
+ size: {
31
+ sm: { panel: '[--sv5ui-tour-w:280px]' },
32
+ md: { panel: '[--sv5ui-tour-w:340px]' },
33
+ lg: { panel: '[--sv5ui-tour-w:420px]' }
34
+ },
35
+ transition: {
36
+ true: {
37
+ panel: [
38
+ 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
39
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95'
40
+ ],
41
+ spotlight: 'transition-transform duration-300 ease-out'
42
+ }
43
+ },
44
+ active: {
45
+ true: { progressDot: 'bg-primary' }
46
+ }
47
+ },
48
+ defaultVariants: {
49
+ size: 'md',
50
+ transition: true
51
+ }
52
+ });
53
+ export const tourDefaults = {
54
+ defaultVariants: tourVariants.defaultVariants,
55
+ slots: {}
56
+ };
package/dist/config.d.ts CHANGED
@@ -30,6 +30,8 @@ export declare const iconsDefaults: {
30
30
  chevronsRight: string;
31
31
  ellipsis: string;
32
32
  close: string;
33
+ menu: string;
34
+ external: string;
33
35
  check: string;
34
36
  light: string;
35
37
  dark: string;
package/dist/config.js CHANGED
@@ -31,6 +31,8 @@ export const iconsDefaults = {
31
31
  chevronsRight: 'lucide:chevrons-right',
32
32
  ellipsis: 'lucide:ellipsis',
33
33
  close: 'lucide:x',
34
+ menu: 'lucide:menu',
35
+ external: 'lucide:arrow-up-right',
34
36
  check: 'lucide:check',
35
37
  light: 'lucide:sun',
36
38
  dark: 'lucide:moon',
@@ -14,3 +14,4 @@ export * from './useIntersectionObserver/index.js';
14
14
  export * from './useScrollLock/index.js';
15
15
  export * from './useFocusTrap/index.js';
16
16
  export * from './useLocalStorage/index.js';
17
+ export * from './useTour/index.js';
@@ -14,3 +14,4 @@ export * from './useIntersectionObserver/index.js';
14
14
  export * from './useScrollLock/index.js';
15
15
  export * from './useFocusTrap/index.js';
16
16
  export * from './useLocalStorage/index.js';
17
+ export * from './useTour/index.js';
@@ -0,0 +1 @@
1
+ export { useTour } from './useTour.svelte.js';
@@ -0,0 +1 @@
1
+ export { useTour } from './useTour.svelte.js';
@@ -0,0 +1,29 @@
1
+ import type { TourController, UseTourOptions } from '../../components/Tour/tour.types.js';
2
+ /**
3
+ * Headless controller for the {@link import('../../components/Tour/tour.types.js').TourProps Tour}
4
+ * component. Pure reactive state — no `$effect` — so it is safe to create
5
+ * conditionally and renders nothing on the server.
6
+ *
7
+ * Drive the tour through the returned {@link TourController}, and pass it to
8
+ * `<Tour controller={tour} />`. Persistence (when enabled) keeps an in-progress
9
+ * tour alive across reloads and SPA navigations, which is what makes multi-page
10
+ * tours possible.
11
+ *
12
+ * @example
13
+ * ```svelte
14
+ * <script>
15
+ * import { useTour, Tour } from 'sv5ui'
16
+ * import { goto } from '$app/navigation'
17
+ *
18
+ * const tour = useTour({
19
+ * steps,
20
+ * persist: true,
21
+ * onStepChange: (i) => { if (steps[i].route) goto(steps[i].route) }
22
+ * })
23
+ * </script>
24
+ *
25
+ * <Button onclick={() => tour.start()}>Start</Button>
26
+ * <Tour {steps} controller={tour} />
27
+ * ```
28
+ */
29
+ export declare function useTour(options: UseTourOptions): TourController;
@@ -0,0 +1,194 @@
1
+ function normalizePersist(persist) {
2
+ if (!persist)
3
+ return null;
4
+ const opts = persist === true ? {} : persist;
5
+ return { key: opts.key ?? 'sv5ui-tour', storage: opts.storage ?? 'local' };
6
+ }
7
+ /**
8
+ * Headless controller for the {@link import('../../components/Tour/tour.types.js').TourProps Tour}
9
+ * component. Pure reactive state — no `$effect` — so it is safe to create
10
+ * conditionally and renders nothing on the server.
11
+ *
12
+ * Drive the tour through the returned {@link TourController}, and pass it to
13
+ * `<Tour controller={tour} />`. Persistence (when enabled) keeps an in-progress
14
+ * tour alive across reloads and SPA navigations, which is what makes multi-page
15
+ * tours possible.
16
+ *
17
+ * @example
18
+ * ```svelte
19
+ * <script>
20
+ * import { useTour, Tour } from 'sv5ui'
21
+ * import { goto } from '$app/navigation'
22
+ *
23
+ * const tour = useTour({
24
+ * steps,
25
+ * persist: true,
26
+ * onStepChange: (i) => { if (steps[i].route) goto(steps[i].route) }
27
+ * })
28
+ * </script>
29
+ *
30
+ * <Button onclick={() => tour.start()}>Start</Button>
31
+ * <Tour {steps} controller={tour} />
32
+ * ```
33
+ */
34
+ export function useTour(options) {
35
+ const defaultStep = options.defaultStep ?? 0;
36
+ const persist = normalizePersist(options.persist);
37
+ let open = $state(false);
38
+ let index = $state(defaultStep);
39
+ let completed = false;
40
+ function getSteps() {
41
+ return options.steps;
42
+ }
43
+ function clamp(i) {
44
+ const last = getSteps().length - 1;
45
+ if (last < 0)
46
+ return 0;
47
+ return Math.max(0, Math.min(i, last));
48
+ }
49
+ function resolveIndex(step) {
50
+ if (typeof step === 'number')
51
+ return clamp(step);
52
+ const found = getSteps().findIndex((s) => s.id === step);
53
+ return found >= 0 ? found : index;
54
+ }
55
+ function nextEnabled(from, dir) {
56
+ const steps = getSteps();
57
+ let i = from + dir;
58
+ while (i >= 0 && i < steps.length) {
59
+ if (!steps[i]?.disabled)
60
+ return i;
61
+ i += dir;
62
+ }
63
+ return null;
64
+ }
65
+ function firstEnabled(from) {
66
+ if (!getSteps()[from]?.disabled)
67
+ return from;
68
+ return nextEnabled(from, 1) ?? nextEnabled(from, -1) ?? from;
69
+ }
70
+ function persistSave() {
71
+ if (!persist || typeof window === 'undefined')
72
+ return;
73
+ try {
74
+ const store = persist.storage === 'session' ? sessionStorage : localStorage;
75
+ if (open) {
76
+ store.setItem(persist.key, JSON.stringify({ open: true, index }));
77
+ }
78
+ else {
79
+ store.removeItem(persist.key);
80
+ }
81
+ }
82
+ catch {
83
+ void 0;
84
+ }
85
+ }
86
+ if (persist && typeof window !== 'undefined') {
87
+ try {
88
+ const store = persist.storage === 'session' ? sessionStorage : localStorage;
89
+ const raw = store.getItem(persist.key);
90
+ if (raw) {
91
+ const parsed = JSON.parse(raw);
92
+ if (parsed?.open) {
93
+ open = true;
94
+ index = clamp(parsed.index ?? defaultStep);
95
+ }
96
+ }
97
+ }
98
+ catch {
99
+ void 0;
100
+ }
101
+ }
102
+ function changeIndex(target) {
103
+ const prev = index;
104
+ index = target;
105
+ persistSave();
106
+ if (target !== prev)
107
+ options.onStepChange?.(target, prev);
108
+ }
109
+ function start(step) {
110
+ const base = step !== undefined ? resolveIndex(step) : clamp(index);
111
+ const target = firstEnabled(base);
112
+ completed = false;
113
+ const prev = open ? index : -1;
114
+ index = target;
115
+ if (!open) {
116
+ open = true;
117
+ persistSave();
118
+ options.onStart?.();
119
+ }
120
+ else {
121
+ persistSave();
122
+ }
123
+ if (target !== prev)
124
+ options.onStepChange?.(target, prev);
125
+ }
126
+ function stop() {
127
+ if (!open)
128
+ return;
129
+ const at = index;
130
+ open = false;
131
+ persistSave();
132
+ if (!completed)
133
+ options.onSkip?.(at);
134
+ }
135
+ function next() {
136
+ if (!open)
137
+ return;
138
+ const ni = nextEnabled(index, 1);
139
+ if (ni === null) {
140
+ completed = true;
141
+ options.onComplete?.();
142
+ open = false;
143
+ persistSave();
144
+ return;
145
+ }
146
+ changeIndex(ni);
147
+ }
148
+ function prev() {
149
+ if (!open)
150
+ return;
151
+ const pi = nextEnabled(index, -1);
152
+ if (pi === null)
153
+ return;
154
+ changeIndex(pi);
155
+ }
156
+ function goTo(step) {
157
+ if (!open) {
158
+ start(step);
159
+ return;
160
+ }
161
+ changeIndex(firstEnabled(resolveIndex(step)));
162
+ }
163
+ return {
164
+ start,
165
+ stop,
166
+ next,
167
+ prev,
168
+ goTo,
169
+ get isActive() {
170
+ return open;
171
+ },
172
+ get currentIndex() {
173
+ return open ? index : -1;
174
+ },
175
+ get totalSteps() {
176
+ return getSteps().length;
177
+ },
178
+ get hasPrev() {
179
+ return open && nextEnabled(index, -1) !== null;
180
+ },
181
+ get hasNext() {
182
+ return open && nextEnabled(index, 1) !== null;
183
+ },
184
+ get isFirst() {
185
+ return nextEnabled(index, -1) === null;
186
+ },
187
+ get isLast() {
188
+ return nextEnabled(index, 1) === null;
189
+ },
190
+ get currentStepData() {
191
+ return open ? getSteps()[index] : undefined;
192
+ }
193
+ };
194
+ }
package/dist/index.d.ts CHANGED
@@ -14,6 +14,10 @@ export * from './components/Container/index.js';
14
14
  export * from './components/Timeline/index.js';
15
15
  export * from './components/User/index.js';
16
16
  export * from './components/Empty/index.js';
17
+ export * from './components/Error/index.js';
18
+ export * from './components/Footer/index.js';
19
+ export * from './components/Header/index.js';
20
+ export * from './components/Main/index.js';
17
21
  export * from './components/Skeleton/index.js';
18
22
  export * from './components/Drawer/index.js';
19
23
  export * from './components/Tooltip/index.js';
@@ -49,6 +53,7 @@ export * from './components/Toast/index.js';
49
53
  export * from './components/Carousel/index.js';
50
54
  export * from './components/Banner/index.js';
51
55
  export * from './components/Stepper/index.js';
56
+ export * from './components/Tour/index.js';
52
57
  export * from './hooks/index.js';
53
58
  export { defineConfig } from './config.js';
54
59
  export type { UIConfig } from './config.js';
package/dist/index.js CHANGED
@@ -15,6 +15,10 @@ export * from './components/Container/index.js';
15
15
  export * from './components/Timeline/index.js';
16
16
  export * from './components/User/index.js';
17
17
  export * from './components/Empty/index.js';
18
+ export * from './components/Error/index.js';
19
+ export * from './components/Footer/index.js';
20
+ export * from './components/Header/index.js';
21
+ export * from './components/Main/index.js';
18
22
  export * from './components/Skeleton/index.js';
19
23
  export * from './components/Drawer/index.js';
20
24
  export * from './components/Tooltip/index.js';
@@ -50,6 +54,7 @@ export * from './components/Toast/index.js';
50
54
  export * from './components/Carousel/index.js';
51
55
  export * from './components/Banner/index.js';
52
56
  export * from './components/Stepper/index.js';
57
+ export * from './components/Tour/index.js';
53
58
  // Composables
54
59
  export * from './hooks/index.js';
55
60
  // Configuration
package/dist/theme.css CHANGED
@@ -86,6 +86,9 @@
86
86
  --color-inverse-surface: oklch(0.208 0.042 265.75);
87
87
  --color-inverse-on-surface: oklch(0.929 0.013 255.51);
88
88
  --color-inverse-primary: oklch(0.707 0.165 254.62);
89
+
90
+ /* ========== LAYOUT ========== */
91
+ --ui-header-height: 4rem;
89
92
  }
90
93
 
91
94
  /* ============================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sv5ui",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "A modern Svelte 5 UI component library with Tailwind CSS",
5
5
  "author": "ndlabdev",
6
6
  "license": "MIT",