flowbite-svelte 1.28.3 → 1.29.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 (103) hide show
  1. package/dist/command-palette/CommandPalette.svelte +1 -1
  2. package/dist/command-palette/CommandPalette.svelte.d.ts +1 -1
  3. package/dist/kanban/KanbanCard.svelte +1 -1
  4. package/dist/kanban/KanbanCard.svelte.d.ts +1 -1
  5. package/dist/scroll-spy/ScrollSpy.svelte +1 -1
  6. package/dist/scroll-spy/ScrollSpy.svelte.d.ts +1 -1
  7. package/dist/split-pane/Divider.svelte +1 -1
  8. package/dist/split-pane/Divider.svelte.d.ts +1 -1
  9. package/dist/split-pane/Pane.svelte +1 -1
  10. package/dist/split-pane/Pane.svelte.d.ts +1 -1
  11. package/dist/split-pane/SplitPane.svelte +1 -1
  12. package/dist/split-pane/SplitPane.svelte.d.ts +1 -1
  13. package/dist/stepper/BreadcrumbStepper.svelte +118 -33
  14. package/dist/stepper/BreadcrumbStepper.svelte.d.ts +6 -3
  15. package/dist/stepper/CheckmarkIcon.svelte +92 -0
  16. package/dist/stepper/CheckmarkIcon.svelte.d.ts +18 -0
  17. package/dist/stepper/DetailedStepper.svelte +133 -27
  18. package/dist/stepper/DetailedStepper.svelte.d.ts +7 -4
  19. package/dist/stepper/DoubleArrowIcon.svelte +34 -0
  20. package/dist/stepper/DoubleArrowIcon.svelte.d.ts +15 -0
  21. package/dist/stepper/ProfileCardIcon.svelte +25 -0
  22. package/dist/stepper/ProfileCardIcon.svelte.d.ts +15 -0
  23. package/dist/stepper/ProgressStepper.svelte +135 -34
  24. package/dist/stepper/ProgressStepper.svelte.d.ts +7 -4
  25. package/dist/stepper/Stepper.svelte +87 -33
  26. package/dist/stepper/Stepper.svelte.d.ts +6 -3
  27. package/dist/stepper/TimelineStepper.svelte +95 -35
  28. package/dist/stepper/TimelineStepper.svelte.d.ts +6 -3
  29. package/dist/stepper/VerticalStepper.svelte +91 -29
  30. package/dist/stepper/VerticalStepper.svelte.d.ts +6 -3
  31. package/dist/stepper/index.d.ts +3 -0
  32. package/dist/stepper/index.js +3 -0
  33. package/dist/stepper/theme.d.ts +6 -18
  34. package/dist/stepper/theme.js +18 -23
  35. package/dist/table/Table.svelte +1 -1
  36. package/dist/table/Table.svelte.d.ts +1 -1
  37. package/dist/table/TableBody.svelte +1 -1
  38. package/dist/table/TableBody.svelte.d.ts +1 -1
  39. package/dist/table/TableBodyCell.svelte +1 -1
  40. package/dist/table/TableBodyCell.svelte.d.ts +1 -1
  41. package/dist/table/TableBodyRow.svelte +1 -1
  42. package/dist/table/TableBodyRow.svelte.d.ts +1 -1
  43. package/dist/table/TableHead.svelte +1 -1
  44. package/dist/table/TableHead.svelte.d.ts +1 -1
  45. package/dist/table/TableHeadCell.svelte +1 -1
  46. package/dist/table/TableHeadCell.svelte.d.ts +1 -1
  47. package/dist/table/TableSearch.svelte +1 -1
  48. package/dist/table/TableSearch.svelte.d.ts +1 -1
  49. package/dist/tabs/TabItem.svelte +1 -1
  50. package/dist/tabs/TabItem.svelte.d.ts +1 -1
  51. package/dist/tabs/Tabs.svelte +1 -1
  52. package/dist/tabs/Tabs.svelte.d.ts +1 -1
  53. package/dist/timeline/Activity.svelte +1 -1
  54. package/dist/timeline/Activity.svelte.d.ts +1 -1
  55. package/dist/timeline/ActivityItem.svelte +1 -1
  56. package/dist/timeline/ActivityItem.svelte.d.ts +1 -1
  57. package/dist/timeline/Group.svelte +1 -1
  58. package/dist/timeline/Group.svelte.d.ts +1 -1
  59. package/dist/timeline/GroupItem.svelte +1 -1
  60. package/dist/timeline/GroupItem.svelte.d.ts +1 -1
  61. package/dist/timeline/Timeline.svelte +1 -1
  62. package/dist/timeline/Timeline.svelte.d.ts +1 -1
  63. package/dist/timeline/TimelineItem.svelte +1 -1
  64. package/dist/timeline/TimelineItem.svelte.d.ts +1 -1
  65. package/dist/toast/Toast.svelte +1 -1
  66. package/dist/toast/Toast.svelte.d.ts +1 -1
  67. package/dist/toast/ToastContainer.svelte +1 -1
  68. package/dist/toast/ToastContainer.svelte.d.ts +1 -1
  69. package/dist/tooltip/Tooltip.svelte +1 -1
  70. package/dist/tooltip/Tooltip.svelte.d.ts +1 -1
  71. package/dist/types.d.ts +52 -14
  72. package/dist/typography/a/A.svelte +1 -1
  73. package/dist/typography/a/A.svelte.d.ts +1 -1
  74. package/dist/typography/blockquote/Blockquote.svelte +1 -1
  75. package/dist/typography/blockquote/Blockquote.svelte.d.ts +1 -1
  76. package/dist/typography/descriptionlist/DescriptionList.svelte +1 -1
  77. package/dist/typography/descriptionlist/DescriptionList.svelte.d.ts +1 -1
  78. package/dist/typography/heading/Heading.svelte +1 -1
  79. package/dist/typography/heading/Heading.svelte.d.ts +1 -1
  80. package/dist/typography/img/Img.svelte +1 -1
  81. package/dist/typography/img/Img.svelte.d.ts +1 -1
  82. package/dist/typography/layout/Layout.svelte +1 -1
  83. package/dist/typography/layout/Layout.svelte.d.ts +1 -1
  84. package/dist/typography/list/Li.svelte +1 -1
  85. package/dist/typography/list/Li.svelte.d.ts +1 -1
  86. package/dist/typography/list/List.svelte +1 -1
  87. package/dist/typography/list/List.svelte.d.ts +1 -1
  88. package/dist/typography/list/theme.js +1 -1
  89. package/dist/typography/mark/Mark.svelte +1 -1
  90. package/dist/typography/mark/Mark.svelte.d.ts +1 -1
  91. package/dist/typography/paragraph/P.svelte +1 -1
  92. package/dist/typography/paragraph/P.svelte.d.ts +1 -1
  93. package/dist/typography/secondary/Secondary.svelte +1 -1
  94. package/dist/typography/secondary/Secondary.svelte.d.ts +1 -1
  95. package/dist/typography/span/Span.svelte +1 -1
  96. package/dist/typography/span/Span.svelte.d.ts +1 -1
  97. package/dist/utils/Arrow.svelte +1 -1
  98. package/dist/utils/Arrow.svelte.d.ts +1 -1
  99. package/dist/utils/Popper.svelte +1 -1
  100. package/dist/utils/Popper.svelte.d.ts +1 -1
  101. package/dist/video/Video.svelte +1 -1
  102. package/dist/video/Video.svelte.d.ts +1 -1
  103. package/package.json +15 -3
@@ -0,0 +1,34 @@
1
+ <script lang="ts">
2
+ import type { SVGAttributes } from "svelte/elements";
3
+
4
+ interface Props extends SVGAttributes<SVGSVGElement> {
5
+ class?: string;
6
+ }
7
+
8
+ let { class: className, ...restProps }: Props = $props();
9
+ </script>
10
+
11
+ <svg
12
+ class={className || "ms-2 h-3 w-3 sm:ms-4 rtl:rotate-180"}
13
+ aria-hidden="true"
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ fill="none"
16
+ viewBox="0 0 12 10"
17
+ stroke="currentColor"
18
+ stroke-linecap="round"
19
+ stroke-linejoin="round"
20
+ stroke-width="2"
21
+ {...restProps}
22
+ >
23
+ <path d="m7 9 4-4-4-4M1 9l4-4-4-4" />
24
+ </svg>
25
+
26
+ <!--
27
+ @component
28
+ [Go to docs](https://flowbite-svelte.com/)
29
+ ## Type
30
+ Props
31
+ ## Props
32
+ @prop class: className
33
+ @prop ...restProps
34
+ -->
@@ -0,0 +1,15 @@
1
+ import type { SVGAttributes } from "svelte/elements";
2
+ interface Props extends SVGAttributes<SVGSVGElement> {
3
+ class?: string;
4
+ }
5
+ /**
6
+ * [Go to docs](https://flowbite-svelte.com/)
7
+ * ## Type
8
+ * Props
9
+ * ## Props
10
+ * @prop class: className
11
+ * @prop ...restProps
12
+ */
13
+ declare const DoubleArrowIcon: import("svelte").Component<Props, {}, "">;
14
+ type DoubleArrowIcon = ReturnType<typeof DoubleArrowIcon>;
15
+ export default DoubleArrowIcon;
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ import type { SVGAttributes } from "svelte/elements";
3
+
4
+ interface Props extends SVGAttributes<SVGSVGElement> {
5
+ class?: string;
6
+ }
7
+
8
+ let { class: className, ...restProps }: Props = $props();
9
+ </script>
10
+
11
+ <svg class={className || "h-3.5 w-3.5 text-gray-500 dark:text-gray-400"} aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 16" {...restProps}>
12
+ <path
13
+ d="M18 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2ZM6.5 3a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5ZM3.014 13.021l.157-.625A3.427 3.427 0 0 1 6.5 9.571a3.426 3.426 0 0 1 3.322 2.805l.159.622-6.967.023ZM16 12h-3a1 1 0 0 1 0-2h3a1 1 0 0 1 0 2Zm0-3h-3a1 1 0 1 1 0-2h3a1 1 0 1 1 0 2Zm0-3h-3a1 1 0 1 1 0-2h3a1 1 0 1 1 0 2Z"
14
+ />
15
+ </svg>
16
+
17
+ <!--
18
+ @component
19
+ [Go to docs](https://flowbite-svelte.com/)
20
+ ## Type
21
+ Props
22
+ ## Props
23
+ @prop class: className
24
+ @prop ...restProps
25
+ -->
@@ -0,0 +1,15 @@
1
+ import type { SVGAttributes } from "svelte/elements";
2
+ interface Props extends SVGAttributes<SVGSVGElement> {
3
+ class?: string;
4
+ }
5
+ /**
6
+ * [Go to docs](https://flowbite-svelte.com/)
7
+ * ## Type
8
+ * Props
9
+ * ## Props
10
+ * @prop class: className
11
+ * @prop ...restProps
12
+ */
13
+ declare const ProfileCardIcon: import("svelte").Component<Props, {}, "">;
14
+ type ProfileCardIcon = ReturnType<typeof ProfileCardIcon>;
15
+ export default ProfileCardIcon;
@@ -1,64 +1,165 @@
1
1
  <script lang="ts">
2
2
  import { setContext } from "svelte";
3
+ import CheckmarkIcon from "./CheckmarkIcon.svelte";
3
4
  import { progressStepper } from "./theme";
4
5
  import type { ProgressStepperProps } from "../types";
5
6
  import clsx from "clsx";
6
7
  import { getTheme } from "../theme/themeUtils";
8
+ import { Tween } from "svelte/motion";
9
+ import { cubicOut } from "svelte/easing";
7
10
 
8
- let { children, steps = [], class: className, classes, ...restrorps }: ProgressStepperProps = $props();
11
+ let { steps = [], class: className, classes, current = $bindable(0), clickable = true, showCheckmarkForCompleted = true, onStepClick, ...restProps }: ProgressStepperProps = $props();
12
+
13
+ // Ensure current is within valid bounds
14
+ $effect(() => {
15
+ if (current < 0) current = 0;
16
+ if (current > steps.length && steps.length > 0) current = steps.length;
17
+ });
18
+
19
+ // Animated progress with Tween
20
+ const animatedProgress = new Tween(0, {
21
+ duration: 100,
22
+ easing: cubicOut
23
+ });
24
+
25
+ // Update animated progress when current changes
26
+ $effect(() => {
27
+ if (steps.length <= 1 || current === 0) {
28
+ animatedProgress.target = 0;
29
+ } else {
30
+ const progressPercent = ((current - 1) / (steps.length - 1)) * 100;
31
+ animatedProgress.target = progressPercent;
32
+ }
33
+ });
9
34
 
10
35
  const theme = getTheme("progressStepper");
11
36
 
12
37
  setContext("stepperType", "progress");
13
38
 
14
- const { base, item, circle } = $derived(progressStepper());
39
+ const { base, item, circle, line, progressLine } = $derived(progressStepper());
40
+
41
+ // Handle step click
42
+ function handleStepClick(stepIndex: number) {
43
+ if (clickable && stepIndex < steps.length) {
44
+ const last = current;
45
+ // Convert 0-based array index to 1-based current value
46
+ current = stepIndex + 1;
47
+
48
+ // Call custom onStepClick if provided
49
+ if (onStepClick) {
50
+ onStepClick({ current, last });
51
+ }
52
+ }
53
+ }
54
+
55
+ // Determine step status - reactive to current changes
56
+ // current = 0: no items highlighted (all pending)
57
+ // current = 1: first item is current
58
+ // current = 2: first is completed, second is current
59
+ function getStepStatus(stepIndex: number): "completed" | "current" | "pending" {
60
+ if (current === 0) {
61
+ return "pending";
62
+ }
63
+ if (stepIndex < current - 1) {
64
+ return "completed";
65
+ } else if (stepIndex === current - 1) {
66
+ return "current";
67
+ } else {
68
+ return "pending";
69
+ }
70
+ }
71
+
72
+ // Calculate line positions and progress
73
+ // Lines should start from center of first circle and end at center of last circle
74
+ const lineStart = $derived(steps.length <= 1 ? "0" : `${(1 / steps.length) * 50}%`);
75
+
76
+ const lineWidth = $derived(steps.length <= 1 ? "0" : `${100 - (1 / steps.length) * 100}%`);
77
+
78
+ // Calculate progress width using animated value
79
+ const progressWidth = $derived(steps.length <= 1 || lineWidth === "0" ? "0" : `${(animatedProgress.current / 100) * parseFloat(lineWidth)}%`);
15
80
  </script>
16
81
 
17
- <ol class={base({ class: clsx(theme?.base, className) })} {...restrorps}>
18
- {#if children}
19
- {@render children()}
20
- {:else if steps}
21
- {#each steps as step, index (step.id)}
22
- <li
23
- class={item({
24
- status: step.status,
25
- isLast: index === steps.length - 1,
26
- class: clsx(theme?.item, classes?.item)
27
- })}
28
- >
29
- <span class={circle({ status: step.status, class: clsx(theme?.circle, classes?.circle) })}>
30
- {#if step.status === "completed"}
31
- {#if step.icon}
32
- <step.icon class={clsx(step.iconClass) || "h-5 w-5 lg:h-6 lg:w-6"} />
33
- {:else}
34
- <svg class="h-3.5 w-3.5 text-blue-600 lg:h-4 lg:w-4 dark:text-blue-300" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 12">
35
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5.917 5.724 10.5 15 1.5" />
36
- </svg>
37
- {/if}
82
+ <ol class={base({ class: clsx(theme?.base, className) })} {...restProps}>
83
+ <!-- Background line (gray) - from center of first to center of last circle -->
84
+ <div class={line({ class: clsx(theme?.line, classes?.line) })} style="left: {lineStart}; width: {lineWidth}" aria-hidden="true"></div>
85
+
86
+ <!-- Progress line (colored, overlays the background) -->
87
+ <div class={progressLine({ class: clsx(theme?.progressLine, classes?.progressLine) })} style="left: {lineStart}; width: {progressWidth}" aria-hidden="true"></div>
88
+
89
+ {#each steps as step, index (step.id)}
90
+ {@const status = step.status ?? getStepStatus(index)}
91
+ <li
92
+ class={item({
93
+ status,
94
+ class: clsx(theme?.item, classes?.item)
95
+ })}
96
+ >
97
+ {#if clickable}
98
+ <button
99
+ type="button"
100
+ class={circle({ status, class: clsx(theme?.circle, classes?.circle, "cursor-pointer transition-all hover:brightness-110") })}
101
+ onclick={() => handleStepClick(index)}
102
+ aria-current={status === "current" ? "step" : undefined}
103
+ >
104
+ {#if status === "completed" && showCheckmarkForCompleted}
105
+ <!-- Checkmark for completed steps -->
106
+ <CheckmarkIcon variant="tick" />
107
+ {:else if step.icon}
108
+ <!-- Show icon if provided -->
109
+ <step.icon class={clsx(step.iconClass) || "h-5 w-5 lg:h-6 lg:w-6"} />
110
+ {:else}
111
+ <!-- Show number for steps without icon -->
112
+ <span class="text-sm font-semibold">{step.id}</span>
113
+ {/if}
114
+ </button>
115
+ {:else}
116
+ <span class={circle({ status, class: clsx(theme?.circle, classes?.circle) })} aria-current={status === "current" ? "step" : undefined}>
117
+ {#if status === "completed" && showCheckmarkForCompleted}
118
+ <!-- Checkmark for completed steps -->
119
+ <CheckmarkIcon variant="tick" />
38
120
  {:else if step.icon}
121
+ <!-- Show icon if provided -->
39
122
  <step.icon class={clsx(step.iconClass) || "h-5 w-5 lg:h-6 lg:w-6"} />
40
123
  {:else}
41
- <svg class="h-4 w-4 text-gray-500 lg:h-5 lg:w-5 dark:text-gray-100" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 16">
42
- <path
43
- d="M18 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2ZM6.5 3a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5ZM3.014 13.021l.157-.625A3.427 3.427 0 0 1 6.5 9.571a3.426 3.426 0 0 1 3.322 2.805l.159.622-6.967.023ZM16 12h-3a1 1 0 0 1 0-2h3a1 1 0 0 1 0 2Zm0-3h-3a1 1 0 1 1 0-2h3a1 1 0 1 1 0 2Zm0-3h-3a1 1 0 1 1 0-2h3a1 1 0 1 1 0 2Z"
44
- />
45
- </svg>
124
+ <!-- Show number for steps without icon -->
125
+ <span class="text-sm font-semibold">{step.id}</span>
46
126
  {/if}
47
127
  </span>
48
- </li>
49
- {/each}
50
- {/if}
128
+ {/if}
129
+ </li>
130
+ {/each}
51
131
  </ol>
52
132
 
133
+ <!--
134
+ ## Features
135
+ - **Clickable navigation**: Click or press Enter/Space on steps to navigate
136
+ - **Auto status**: Automatically determines completed/current/pending status based on current index
137
+ - **Numbers by default**: Shows step numbers (from step.id) when no icon is provided
138
+ - **Custom icons**: Icons replace numbers when provided
139
+ - **Checkmarks**: Completed steps show checkmarks (can be disabled with showCheckmarkForCompleted={false})
140
+ - **Progress line**: A colored overlay line shows progress up to the current step
141
+ - **Accessible**: Keyboard navigation with proper ARIA attributes
142
+
143
+ ## Note
144
+ The `current` prop is 1-based:
145
+ - current=0 means no step is active (all pending)
146
+ - current=1 means first step is active
147
+ - current=2 means first step is completed, second step is active
148
+ - etc.
149
+ -->
150
+
53
151
  <!--
54
152
  @component
55
153
  [Go to docs](https://flowbite-svelte.com/)
56
154
  ## Type
57
- [ProgressStepperProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1609)
155
+ [ProgressStepperProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1614)
58
156
  ## Props
59
- @prop children
60
157
  @prop steps = []
61
158
  @prop class: className
62
159
  @prop classes
63
- @prop ...restrorps
160
+ @prop current = $bindable(0)
161
+ @prop clickable = true
162
+ @prop showCheckmarkForCompleted = true
163
+ @prop onStepClick
164
+ @prop ...restProps
64
165
  -->
@@ -2,14 +2,17 @@ import type { ProgressStepperProps } from "../types";
2
2
  /**
3
3
  * [Go to docs](https://flowbite-svelte.com/)
4
4
  * ## Type
5
- * [ProgressStepperProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1609)
5
+ * [ProgressStepperProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1614)
6
6
  * ## Props
7
- * @prop children
8
7
  * @prop steps = []
9
8
  * @prop class: className
10
9
  * @prop classes
11
- * @prop ...restrorps
10
+ * @prop current = $bindable(0)
11
+ * @prop clickable = true
12
+ * @prop showCheckmarkForCompleted = true
13
+ * @prop onStepClick
14
+ * @prop ...restProps
12
15
  */
13
- declare const ProgressStepper: import("svelte").Component<ProgressStepperProps, {}, "">;
16
+ declare const ProgressStepper: import("svelte").Component<ProgressStepperProps, {}, "current">;
14
17
  type ProgressStepper = ReturnType<typeof ProgressStepper>;
15
18
  export default ProgressStepper;
@@ -1,68 +1,122 @@
1
1
  <script lang="ts">
2
2
  import { setContext } from "svelte";
3
+ import type { StepStatus, Step } from "../types";
4
+ import CheckmarkIcon from "./CheckmarkIcon.svelte";
3
5
  import { stepper } from "./theme";
4
6
  import type { StepperProps } from "../types";
5
7
  import clsx from "clsx";
6
8
  import { getTheme } from "../theme/themeUtils";
7
9
 
8
- let { children, steps = [], class: className, classes, ...restProps }: StepperProps = $props();
10
+ let { steps = [], class: className, classes, current = $bindable(1), clickable = true, showCheckmarkForCompleted = true, onStepClick, ...restProps }: StepperProps = $props();
11
+
12
+ // Ensure current is within valid bounds
13
+ $effect(() => {
14
+ if (current < 0) current = 0;
15
+ if (current > steps.length && steps.length > 0) current = steps.length;
16
+ });
9
17
 
10
18
  const theme = getTheme("stepper");
11
19
 
12
20
  setContext("stepperType", "stepper");
13
21
 
14
22
  const { base, item, content } = $derived(stepper());
23
+
24
+ // Handle step click
25
+ function handleStepClick(stepIndex: number) {
26
+ if (clickable && stepIndex < steps.length) {
27
+ const last = current;
28
+ // Convert 0-based array index to 1-based current value
29
+ current = stepIndex + 1;
30
+
31
+ // Call custom onStepClick if provided
32
+ if (onStepClick) {
33
+ onStepClick({ current, last });
34
+ }
35
+ }
36
+ }
37
+
38
+ // Determine step status - reactive to current changes
39
+ // current = 0: no items highlighted (all pending)
40
+ // current = 1: first item is current
41
+ // current = 2: first is completed, second is current
42
+ function getStepStatus(stepIndex: number): "completed" | "current" | "pending" {
43
+ if (current === 0) {
44
+ return "pending";
45
+ }
46
+ if (stepIndex < current - 1) {
47
+ return "completed";
48
+ } else if (stepIndex === current - 1) {
49
+ return "current";
50
+ } else {
51
+ return "pending";
52
+ }
53
+ }
15
54
  </script>
16
55
 
56
+ {#snippet stepContent(step: Step, status: StepStatus, index: number)}
57
+ {#if status === "completed" && showCheckmarkForCompleted}
58
+ <CheckmarkIcon />
59
+ {:else if step.icon}
60
+ <step.icon class={clsx(step.iconClass) || "me-2.5 h-3.5 w-3.5 sm:h-4 sm:w-4"} />
61
+ {:else}
62
+ <span class="me-2">{step.id || index + 1}</span>
63
+ {/if}
64
+ {step.label}
65
+ {#if step.description}
66
+ <span class={clsx(step.descriptionClass) || "hidden sm:ms-2 sm:inline-flex"}>{step.description}</span>
67
+ {/if}
68
+ {/snippet}
69
+
17
70
  <ol {...restProps} class={base({ class: clsx(theme?.base, className) })}>
18
- {#if children}
19
- {@render children()}
20
- {:else if steps}
21
- {#each steps as step, index (step.id ?? index)}
22
- <li
23
- class={item({
24
- status: step.status,
25
- isLast: index === steps.length - 1,
26
- class: clsx(theme?.item, classes?.item)
27
- })}
28
- >
71
+ {#each steps as step, index (step.id ?? index)}
72
+ {@const status = step.status ?? getStepStatus(index)}
73
+ <li
74
+ class={item({
75
+ status,
76
+ isLast: index === steps.length - 1,
77
+ class: clsx(theme?.item, classes?.item)
78
+ })}
79
+ >
80
+ {#if clickable}
81
+ <button
82
+ type="button"
83
+ class={content({
84
+ status,
85
+ isLast: index === steps.length - 1,
86
+ class: clsx(theme?.content, classes?.content, "w-full cursor-pointer text-left transition-opacity hover:opacity-75")
87
+ })}
88
+ onclick={() => handleStepClick(index)}
89
+ aria-current={status === "current" ? "step" : undefined}
90
+ >
91
+ {@render stepContent(step, status, index)}
92
+ </button>
93
+ {:else}
29
94
  <span
30
95
  class={content({
31
- status: step.status,
96
+ status,
32
97
  isLast: index === steps.length - 1,
33
98
  class: clsx(theme?.content, classes?.content)
34
99
  })}
35
100
  >
36
- {#if step.status === "completed"}
37
- {#if step.icon}
38
- <step.icon class={clsx(step.iconClass) || "me-2.5 h-3.5 w-3.5 sm:h-4 sm:w-4"} />
39
- {:else}
40
- <svg class="me-2.5 h-3.5 w-3.5 sm:h-4 sm:w-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
41
- <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z" />
42
- </svg>
43
- {/if}
44
- {:else}
45
- <span class="me-2">{step.id || index + 1}</span>
46
- {/if}
47
- {step.label}
48
- {#if step.description}
49
- <span class={clsx(step.descriptionClass) || "hidden sm:ms-2 sm:inline-flex"}>{step.description}</span>
50
- {/if}
101
+ {@render stepContent(step, status, index)}
51
102
  </span>
52
- </li>
53
- {/each}
54
- {/if}
103
+ {/if}
104
+ </li>
105
+ {/each}
55
106
  </ol>
56
107
 
57
108
  <!--
58
109
  @component
59
110
  [Go to docs](https://flowbite-svelte.com/)
60
111
  ## Type
61
- [StepperProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1596)
112
+ [StepperProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1598)
62
113
  ## Props
63
- @prop children
64
114
  @prop steps = []
65
115
  @prop class: className
66
116
  @prop classes
117
+ @prop current = $bindable(1)
118
+ @prop clickable = true
119
+ @prop showCheckmarkForCompleted = true
120
+ @prop onStepClick
67
121
  @prop ...restProps
68
122
  -->
@@ -2,14 +2,17 @@ import type { StepperProps } from "../types";
2
2
  /**
3
3
  * [Go to docs](https://flowbite-svelte.com/)
4
4
  * ## Type
5
- * [StepperProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1596)
5
+ * [StepperProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1598)
6
6
  * ## Props
7
- * @prop children
8
7
  * @prop steps = []
9
8
  * @prop class: className
10
9
  * @prop classes
10
+ * @prop current = $bindable(1)
11
+ * @prop clickable = true
12
+ * @prop showCheckmarkForCompleted = true
13
+ * @prop onStepClick
11
14
  * @prop ...restProps
12
15
  */
13
- declare const Stepper: import("svelte").Component<StepperProps, {}, "">;
16
+ declare const Stepper: import("svelte").Component<StepperProps, {}, "current">;
14
17
  type Stepper = ReturnType<typeof Stepper>;
15
18
  export default Stepper;
@@ -1,65 +1,125 @@
1
1
  <script lang="ts">
2
2
  import { setContext } from "svelte";
3
+ import type { StepStatus, TimelineStep } from "../types";
4
+ import CheckmarkIcon from "./CheckmarkIcon.svelte";
5
+ import ProfileCardIcon from "./ProfileCardIcon.svelte";
3
6
  import { timelineStepper } from "./theme";
4
7
  import type { TimelineStepperProps } from "../types";
5
8
  import clsx from "clsx";
6
9
  import { getTheme } from "../theme/themeUtils";
7
10
 
8
- let { children, steps = [], class: className, classes, contentClass, ...restProps }: TimelineStepperProps = $props();
11
+ let { steps = [], class: className, classes, contentClass, current = $bindable(1), clickable = true, showCheckmarkForCompleted = true, onStepClick, ...restProps }: TimelineStepperProps = $props();
12
+
13
+ // Ensure current is within valid bounds
14
+ $effect(() => {
15
+ if (current < 0) current = 0;
16
+ if (current > steps.length && steps.length > 0) current = steps.length;
17
+ });
9
18
 
10
19
  const theme = getTheme("timelineStepper");
11
20
 
12
21
  setContext("stepperType", "timeline");
13
22
 
14
23
  const { base, item, circle } = $derived(timelineStepper());
24
+
25
+ // Handle step click
26
+ function handleStepClick(stepIndex: number) {
27
+ if (clickable && stepIndex < steps.length) {
28
+ const last = current;
29
+ // Convert 0-based array index to 1-based current value
30
+ current = stepIndex + 1;
31
+
32
+ // Call custom onStepClick if provided
33
+ if (onStepClick) {
34
+ onStepClick({ current, last });
35
+ }
36
+ }
37
+ }
38
+
39
+ // Determine step status - reactive to current changes
40
+ function getStepStatus(stepIndex: number): "completed" | "current" | "pending" {
41
+ if (current === 0) {
42
+ return "pending";
43
+ }
44
+ if (stepIndex < current - 1) {
45
+ return "completed";
46
+ } else if (stepIndex === current - 1) {
47
+ return "current";
48
+ } else {
49
+ return "pending";
50
+ }
51
+ }
15
52
  </script>
16
53
 
54
+ {#snippet stepIcon(status: StepStatus, step: TimelineStep)}
55
+ {#if status === "completed" && showCheckmarkForCompleted}
56
+ <CheckmarkIcon class="h-3.5 w-3.5 text-green-500 dark:text-green-400" />
57
+ {:else if step.icon}
58
+ <step.icon class={clsx(step.iconClass) || "h-3.5 w-3.5"} />
59
+ {:else}
60
+ <ProfileCardIcon />
61
+ {/if}
62
+ {/snippet}
63
+
17
64
  <ol class={base({ class: clsx(theme?.base, className) })} {...restProps}>
18
- {#if children}
19
- {@render children()}
20
- {:else if steps}
21
- {#each steps as step, index (step.id)}
22
- <li class={item({ isLast: index === steps.length - 1, class: clsx(theme?.item, classes?.item) })}>
23
- <span class={circle({ status: step.status, class: clsx(theme?.circle, classes?.circle) })}>
24
- {#if step.status === "completed"}
25
- {#if step.icon}
26
- <step.icon class={clsx(step.iconClass) || "h-3.5 w-3.5"} />
27
- {:else}
28
- <svg class="h-3.5 w-3.5 text-green-500 dark:text-green-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 12">
29
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5.917 5.724 10.5 15 1.5" />
30
- </svg>
31
- {/if}
32
- {:else if step.icon}
33
- <step.icon class={clsx(step.iconClass) || "h-3.5 w-3.5"} />
34
- {:else}
35
- <svg class="h-3.5 w-3.5 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 16">
36
- <path
37
- d="M18 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2ZM6.5 3a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5ZM3.014 13.021l.157-.625A3.427 3.427 0 0 1 6.5 9.571a3.426 3.426 0 0 1 3.322 2.805l.159.622-6.967.023ZM16 12h-3a1 1 0 0 1 0-2h3a1 1 0 0 1 0 2Zm0-3h-3a1 1 0 1 1 0-2h3a1 1 0 1 1 0 2Zm0-3h-3a1 1 0 1 1 0-2h3a1 1 0 1 1 0 2Z"
38
- />
39
- </svg>
40
- {/if}
65
+ {#each steps as step, index (step.id)}
66
+ {@const status = step.status ?? getStepStatus(index)}
67
+ <li class={item({ isLast: index === steps.length - 1, class: clsx(theme?.item, classes?.item) })}>
68
+ {#if clickable}
69
+ <button
70
+ type="button"
71
+ class="absolute -start-4 flex h-8 w-8 cursor-pointer items-center justify-center rounded-full ring-4 ring-white transition-opacity hover:opacity-75 dark:ring-gray-900 {circle({
72
+ status,
73
+ class: clsx(theme?.circle, classes?.circle)
74
+ })}"
75
+ onclick={() => handleStepClick(index)}
76
+ aria-current={status === "current" ? "step" : undefined}
77
+ >
78
+ {@render stepIcon(status, step)}
79
+ </button>
80
+ {:else}
81
+ <span class={circle({ status, class: clsx(theme?.circle, classes?.circle) })} aria-current={status === "current" ? "step" : undefined}>
82
+ {@render stepIcon(status, step)}
41
83
  </span>
42
- <div class={clsx(contentClass)}>
43
- <h3 class="leading-tight font-medium">{step.label}</h3>
44
- {#if step.description}
45
- <p class="text-sm">{step.description}</p>
46
- {/if}
47
- </div>
48
- </li>
49
- {/each}
50
- {/if}
84
+ {/if}
85
+ <div class={clsx(contentClass)}>
86
+ <h3 class="leading-tight font-medium">{step.label}</h3>
87
+ {#if step.description}
88
+ <p class="text-sm">{step.description}</p>
89
+ {/if}
90
+ </div>
91
+ </li>
92
+ {/each}
51
93
  </ol>
52
94
 
95
+ <!--
96
+ ## Features
97
+ - **Clickable navigation**: Click or press Enter/Space on steps to navigate
98
+ - **Auto status**: Automatically determines completed/current/pending status based on current index
99
+ - **Custom icons**: Support for custom icons on completed steps
100
+ - **Accessible**: Keyboard navigation with proper ARIA attributes
101
+
102
+ ## Note
103
+ The `current` prop is 1-based:
104
+ - current=0 means no step is active (all pending)
105
+ - current=1 means first step is active
106
+ - current=2 means first step is completed, second step is active
107
+ - etc.
108
+ -->
109
+
53
110
  <!--
54
111
  @component
55
112
  [Go to docs](https://flowbite-svelte.com/)
56
113
  ## Type
57
- [TimelineStepperProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1670)
114
+ [TimelineStepperProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1687)
58
115
  ## Props
59
- @prop children
60
116
  @prop steps = []
61
117
  @prop class: className
62
118
  @prop classes
63
119
  @prop contentClass
120
+ @prop current = $bindable(1)
121
+ @prop clickable = true
122
+ @prop showCheckmarkForCompleted = true
123
+ @prop onStepClick
64
124
  @prop ...restProps
65
125
  -->