@makolabs/ripple 2.5.9 → 3.0.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.
Files changed (186) hide show
  1. package/README.md +403 -497
  2. package/dist/adapters/storage/S3Adapter.d.ts +49 -1
  3. package/dist/adapters/storage/S3Adapter.js +38 -1
  4. package/dist/adapters/storage/types.d.ts +20 -0
  5. package/dist/ai/AIChatInterface.svelte +2 -1
  6. package/dist/ai/AIChatInterface.svelte.d.ts +2 -1
  7. package/dist/ai/CodeRenderer.svelte +7 -2
  8. package/dist/ai/CodeRenderer.svelte.d.ts +2 -1
  9. package/dist/ai/ComposeDropdown.svelte +1 -1
  10. package/dist/ai/MessageBox.svelte +3 -3
  11. package/dist/ai/MessageBox.svelte.d.ts +3 -2
  12. package/dist/ai/ThinkingDisplay.svelte +4 -3
  13. package/dist/ai/ThinkingDisplay.svelte.d.ts +2 -1
  14. package/dist/ai/ai-types.d.ts +55 -1
  15. package/dist/button/Button.svelte +5 -5
  16. package/dist/button/button-types.d.ts +49 -4
  17. package/dist/button/button.d.ts +9 -9
  18. package/dist/button/button.js +6 -6
  19. package/dist/charts/Chart.svelte +8 -16
  20. package/dist/charts/chart-types.d.ts +78 -1
  21. package/dist/drawer/Drawer.svelte +6 -26
  22. package/dist/drawer/drawer-types.d.ts +33 -12
  23. package/dist/drawer/drawer.d.ts +3 -3
  24. package/dist/drawer/drawer.js +1 -1
  25. package/dist/elements/accordion/Accordion.svelte +6 -17
  26. package/dist/elements/accordion/accordion-types.d.ts +53 -6
  27. package/dist/elements/alert/Alert.svelte +3 -0
  28. package/dist/elements/badge/Badge.svelte +1 -1
  29. package/dist/elements/badge/badge-types.d.ts +22 -0
  30. package/dist/elements/badge/badge.d.ts +3 -3
  31. package/dist/elements/badge/badge.js +1 -1
  32. package/dist/elements/combobox/ComboBox.svelte +244 -0
  33. package/dist/elements/combobox/ComboBox.svelte.d.ts +4 -0
  34. package/dist/elements/combobox/combobox-types.d.ts +41 -0
  35. package/dist/elements/combobox/combobox-types.js +1 -0
  36. package/dist/elements/context-menu/ContextMenu.svelte +137 -0
  37. package/dist/elements/context-menu/ContextMenu.svelte.d.ts +4 -0
  38. package/dist/elements/context-menu/context-menu-types.d.ts +40 -0
  39. package/dist/elements/context-menu/context-menu-types.js +1 -0
  40. package/dist/elements/dropdown/Dropdown.svelte +1 -1
  41. package/dist/elements/dropdown/Select.svelte +4 -1
  42. package/dist/elements/dropdown/dropdown-types.d.ts +114 -0
  43. package/dist/elements/dropdown/dropdown.d.ts +3 -3
  44. package/dist/elements/dropdown/dropdown.js +2 -2
  45. package/dist/elements/dropdown/select.d.ts +3 -108
  46. package/dist/elements/dropdown/select.js +38 -47
  47. package/dist/elements/empty-state/EmptyState.svelte +1 -1
  48. package/dist/elements/empty-state/empty-state-types.d.ts +32 -1
  49. package/dist/elements/empty-state/empty-state.d.ts +3 -3
  50. package/dist/elements/empty-state/empty-state.js +2 -2
  51. package/dist/elements/file-upload/FileUpload.svelte +5 -0
  52. package/dist/elements/file-upload/file-upload-types.d.ts +59 -0
  53. package/dist/elements/pagination/Pagination.svelte +53 -21
  54. package/dist/elements/pagination/Pagination.svelte.d.ts +33 -5
  55. package/dist/elements/popover/Popover.svelte +254 -0
  56. package/dist/elements/popover/Popover.svelte.d.ts +4 -0
  57. package/dist/elements/popover/index.d.ts +2 -0
  58. package/dist/elements/popover/index.js +1 -0
  59. package/dist/elements/popover/popover-types.d.ts +60 -0
  60. package/dist/elements/popover/popover-types.js +1 -0
  61. package/dist/elements/progress/Progress.svelte +32 -7
  62. package/dist/elements/progress/progress-types.d.ts +48 -1
  63. package/dist/elements/skeleton/Skeleton.svelte +56 -0
  64. package/dist/elements/skeleton/Skeleton.svelte.d.ts +4 -0
  65. package/dist/elements/skeleton/index.d.ts +2 -0
  66. package/dist/elements/skeleton/index.js +1 -0
  67. package/dist/elements/skeleton/skeleton-types.d.ts +50 -0
  68. package/dist/elements/skeleton/skeleton-types.js +1 -0
  69. package/dist/elements/spinner/Spinner.svelte +1 -1
  70. package/dist/elements/spinner/spinner-types.d.ts +20 -0
  71. package/dist/elements/spinner/spinner.d.ts +3 -3
  72. package/dist/elements/spinner/spinner.js +2 -2
  73. package/dist/elements/tooltip/Tooltip.svelte +108 -11
  74. package/dist/elements/tooltip/tooltip-types.d.ts +49 -1
  75. package/dist/file-browser/FileBrowser.svelte +21 -12
  76. package/dist/filters/CompactFilters.svelte +221 -33
  77. package/dist/filters/CompactFilters.svelte.d.ts +1 -1
  78. package/dist/filters/FilterBar.svelte +184 -0
  79. package/dist/filters/FilterBar.svelte.d.ts +4 -0
  80. package/dist/filters/FilterPopover.svelte +346 -0
  81. package/dist/filters/FilterPopover.svelte.d.ts +4 -0
  82. package/dist/filters/date-presets.d.ts +15 -0
  83. package/dist/filters/date-presets.js +107 -0
  84. package/dist/filters/filter-types.d.ts +69 -3
  85. package/dist/filters/index.d.ts +5 -0
  86. package/dist/filters/index.js +4 -0
  87. package/dist/filters/sync-filters-to-url.svelte.d.ts +37 -0
  88. package/dist/filters/sync-filters-to-url.svelte.js +114 -0
  89. package/dist/forms/Checkbox.svelte +24 -9
  90. package/dist/forms/DateRange.svelte +23 -6
  91. package/dist/forms/Input.svelte +19 -19
  92. package/dist/forms/MarketSelector.svelte +9 -4
  93. package/dist/forms/NumberInput.svelte +14 -18
  94. package/dist/forms/RadioGroup.svelte +127 -0
  95. package/dist/forms/RadioGroup.svelte.d.ts +4 -0
  96. package/dist/forms/SegmentedControl.svelte +11 -4
  97. package/dist/forms/Slider.svelte +72 -3
  98. package/dist/forms/Tags.svelte +44 -14
  99. package/dist/forms/Textarea.svelte +121 -0
  100. package/dist/forms/Textarea.svelte.d.ts +4 -0
  101. package/dist/forms/Toggle.svelte +30 -22
  102. package/dist/forms/calendar/Calendar.svelte +315 -0
  103. package/dist/forms/calendar/Calendar.svelte.d.ts +4 -0
  104. package/dist/forms/calendar/calendar-types.d.ts +54 -0
  105. package/dist/forms/calendar/calendar-types.js +1 -0
  106. package/dist/forms/calendar/index.d.ts +2 -0
  107. package/dist/forms/calendar/index.js +1 -0
  108. package/dist/forms/date-picker/DatePicker.svelte +141 -0
  109. package/dist/forms/date-picker/DatePicker.svelte.d.ts +4 -0
  110. package/dist/forms/date-picker/date-picker-types.d.ts +29 -0
  111. package/dist/forms/date-picker/date-picker-types.js +1 -0
  112. package/dist/forms/form-size.d.ts +37 -0
  113. package/dist/forms/form-size.js +67 -0
  114. package/dist/forms/form-types.d.ts +430 -6
  115. package/dist/forms/market/market-selector-types.d.ts +52 -1
  116. package/dist/forms/segmented-control.d.ts +5 -2
  117. package/dist/forms/segmented-control.js +25 -13
  118. package/dist/forms/slider.d.ts +3 -3
  119. package/dist/forms/slider.js +37 -30
  120. package/dist/funcs/user-management.remote.js +1 -1
  121. package/dist/header/Breadcrumbs.svelte +4 -20
  122. package/dist/header/PageHeader.svelte +6 -14
  123. package/dist/header/breadcrumbs.d.ts +3 -11
  124. package/dist/header/breadcrumbs.js +10 -5
  125. package/dist/header/header-types.d.ts +62 -11
  126. package/dist/index.d.ts +35 -9
  127. package/dist/index.js +24 -4
  128. package/dist/layout/activity-list/ActivityList.svelte +13 -7
  129. package/dist/layout/activity-list/activity-list-types.d.ts +46 -7
  130. package/dist/layout/card/Card.svelte +12 -15
  131. package/dist/layout/card/MetricCard.svelte +50 -32
  132. package/dist/layout/card/card-types.d.ts +114 -4
  133. package/dist/layout/navbar/navbar-types.d.ts +48 -0
  134. package/dist/layout/navbar/navbar.d.ts +3 -3
  135. package/dist/layout/navbar/navbar.js +2 -2
  136. package/dist/layout/sidebar/Sidebar.svelte +87 -11
  137. package/dist/layout/sidebar/sidebar-types.d.ts +60 -1
  138. package/dist/layout/stepper/Stepper.svelte +288 -0
  139. package/dist/layout/stepper/Stepper.svelte.d.ts +4 -0
  140. package/dist/layout/stepper/stepper-types.d.ts +80 -0
  141. package/dist/layout/stepper/stepper-types.js +1 -0
  142. package/dist/layout/table/Table.svelte +91 -85
  143. package/dist/layout/table/table-types.d.ts +148 -24
  144. package/dist/layout/table/table.d.ts +3 -3
  145. package/dist/layout/table/table.js +2 -2
  146. package/dist/layout/tabs/Tab.svelte +6 -2
  147. package/dist/layout/tabs/Tab.svelte.d.ts +4 -1
  148. package/dist/layout/tabs/TabGroup.svelte +9 -2
  149. package/dist/layout/tabs/tabs-types.d.ts +63 -0
  150. package/dist/layout/tabs/tabs.d.ts +3 -3
  151. package/dist/layout/tabs/tabs.js +12 -6
  152. package/dist/modal/ConfirmDialog.svelte +65 -0
  153. package/dist/modal/ConfirmDialog.svelte.d.ts +4 -0
  154. package/dist/modal/Modal.svelte +6 -26
  155. package/dist/modal/confirm-dialog-types.d.ts +39 -0
  156. package/dist/modal/confirm-dialog-types.js +1 -0
  157. package/dist/modal/modal-types.d.ts +51 -12
  158. package/dist/modal/modal.d.ts +3 -3
  159. package/dist/modal/modal.js +3 -3
  160. package/dist/pipeline/Pipeline.svelte +8 -3
  161. package/dist/pipeline/pipeline-types.d.ts +55 -3
  162. package/dist/pipeline/pipeline.d.ts +18 -3
  163. package/dist/pipeline/pipeline.js +7 -2
  164. package/dist/server/s3.d.ts +35 -3
  165. package/dist/sonner/Toaster.svelte +29 -0
  166. package/dist/sonner/Toaster.svelte.d.ts +4 -0
  167. package/dist/sonner/index.d.ts +21 -0
  168. package/dist/sonner/index.js +20 -0
  169. package/dist/user-management/UserManagement.svelte +22 -16
  170. package/dist/user-management/UserModal.svelte +10 -7
  171. package/dist/user-management/UserTable.svelte +16 -17
  172. package/dist/user-management/UserViewModal.svelte +11 -11
  173. package/dist/user-management/user-management-types.d.ts +118 -31
  174. package/dist/variants.d.ts +1 -1
  175. package/dist/variants.js +1 -1
  176. package/package.json +7 -4
  177. package/dist/config/ai.d.ts +0 -13
  178. package/dist/config/ai.js +0 -44
  179. package/dist/elements/empty-state/EmptyStateTestWrapper.svelte +0 -25
  180. package/dist/elements/empty-state/EmptyStateTestWrapper.svelte.d.ts +0 -8
  181. package/dist/elements/tooltip/TooltipTestWrapper.svelte +0 -14
  182. package/dist/elements/tooltip/TooltipTestWrapper.svelte.d.ts +0 -7
  183. package/dist/helper/deprecation.d.ts +0 -14
  184. package/dist/helper/deprecation.js +0 -24
  185. package/dist/modal/ModalFooterTestWrapper.svelte +0 -17
  186. package/dist/modal/ModalFooterTestWrapper.svelte.d.ts +0 -8
@@ -1,22 +1,69 @@
1
1
  import type { ClassValue } from 'tailwind-variants';
2
2
  import type { VariantColors, VariantSizes } from '../../index.js';
3
+ /** One segment of a multi-segment progress bar (`ProgressProps.segments`). */
3
4
  export type ProgressSegment = {
5
+ /** Segment size. Treated as a share of `max` (so segment values should sum to ≤ `max`). */
4
6
  value: number;
7
+ /** Segment fill color. */
5
8
  color: VariantColors;
9
+ /** Optional label rendered inside the legend when `showLabels` is set. */
6
10
  label?: string;
7
11
  };
12
+ /**
13
+ * Props for `<Progress>` — a horizontal progress bar. Supports two modes:
14
+ * 1. **Single value** — pass `value` (and optionally `max`). Renders one filled bar.
15
+ * 2. **Segmented** — pass `segments: ProgressSegment[]`. Renders a stacked bar with
16
+ * per-segment colors and an optional legend below.
17
+ *
18
+ * For KPI-style displays where the progress bar is part of a card,
19
+ * `<MetricCard>` accepts the same `percent`/`segments` props directly.
20
+ *
21
+ * @example
22
+ * ```svelte
23
+ * <!-- Simple -->
24
+ * <Progress value={68} showLabel />
25
+ * ```
26
+ *
27
+ * @example
28
+ * ```svelte
29
+ * <!-- Multi-segment with legend -->
30
+ * <Progress
31
+ * max={100}
32
+ * segments={[
33
+ * { value: 50, color: 'success', label: 'Healthy' },
34
+ * { value: 30, color: 'warning', label: 'Warning' },
35
+ * { value: 20, color: 'danger', label: 'Critical' }
36
+ * ]}
37
+ * showLabels
38
+ * showValues
39
+ * />
40
+ * ```
41
+ */
8
42
  export type ProgressProps = {
9
- value: number;
43
+ /** Current value (single-mode). Ignored when `segments` is provided. @default 0 */
44
+ value?: number;
45
+ /** Max scale value. @default 100 */
10
46
  max?: number;
11
47
  size?: VariantSizes;
48
+ /** Fill color for single-mode. @default 'primary' */
12
49
  color?: VariantColors;
50
+ /** Show "value / max" label next to the bar (single-mode). @default false */
13
51
  showLabel?: boolean;
52
+ /** Where to render the label. @default 'top' */
14
53
  labelPosition?: 'top' | 'bottom' | 'right';
54
+ /**
55
+ * Segmented bars — when provided, takes precedence over `value`.
56
+ * Each segment renders in its own color, and an optional legend
57
+ * appears below when `showLabels` or `showValues` is set.
58
+ */
15
59
  segments?: ProgressSegment[];
60
+ /** Show the legend under segmented bars. @default false */
16
61
  showLabels?: boolean;
62
+ /** Show numeric values alongside labels in the legend. @default false */
17
63
  showValues?: boolean;
18
64
  class?: ClassValue;
19
65
  labelClass?: ClassValue;
66
+ /** Classes on the inner bar element(s). */
20
67
  barClass?: ClassValue;
21
68
  testId?: string;
22
69
  };
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../helper/cls.js';
3
+ import { buildTestId } from '../../helper/testid.js';
4
+ import type { SkeletonProps } from './skeleton-types.js';
5
+
6
+ let {
7
+ class: className = '',
8
+ variant = 'pulse',
9
+ rounded = 'rounded-md',
10
+ ariaLabel = 'Loading',
11
+ style,
12
+ testId
13
+ }: SkeletonProps = $props();
14
+ </script>
15
+
16
+ <div
17
+ class={cn(
18
+ 'bg-default-200',
19
+ rounded,
20
+ variant === 'pulse' && 'animate-pulse',
21
+ variant === 'shimmer' && 'ripple-skeleton-shimmer overflow-hidden',
22
+ className
23
+ )}
24
+ {style}
25
+ role="status"
26
+ aria-label={ariaLabel}
27
+ aria-busy="true"
28
+ data-testid={buildTestId('skeleton', undefined, testId)}
29
+ ></div>
30
+
31
+ <style>
32
+ /* Shimmer variant: gradient sweep using a CSS animation. Kept here (not Tailwind)
33
+ because moving gradients aren't expressible with Tailwind utilities. */
34
+ .ripple-skeleton-shimmer {
35
+ position: relative;
36
+ isolation: isolate;
37
+ }
38
+ .ripple-skeleton-shimmer::after {
39
+ content: '';
40
+ position: absolute;
41
+ inset: 0;
42
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.55), transparent);
43
+ transform: translateX(-100%);
44
+ animation: ripple-skeleton-shimmer 1.6s infinite;
45
+ }
46
+ @keyframes ripple-skeleton-shimmer {
47
+ 100% {
48
+ transform: translateX(100%);
49
+ }
50
+ }
51
+ @media (prefers-reduced-motion: reduce) {
52
+ .ripple-skeleton-shimmer::after {
53
+ animation: none;
54
+ }
55
+ }
56
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { SkeletonProps } from './skeleton-types.js';
2
+ declare const Skeleton: import("svelte").Component<SkeletonProps, {}, "">;
3
+ type Skeleton = ReturnType<typeof Skeleton>;
4
+ export default Skeleton;
@@ -0,0 +1,2 @@
1
+ export { default as Skeleton } from './Skeleton.svelte';
2
+ export type { SkeletonProps, SkeletonVariant } from './skeleton-types.js';
@@ -0,0 +1 @@
1
+ export { default as Skeleton } from './Skeleton.svelte';
@@ -0,0 +1,50 @@
1
+ import type { ClassValue } from 'tailwind-variants';
2
+ /**
3
+ * Animation style for `<Skeleton>`.
4
+ * - `'pulse'` — subtle opacity pulse (Tailwind's `animate-pulse`)
5
+ * - `'shimmer'` — gradient slides across the block
6
+ */
7
+ export type SkeletonVariant = 'pulse' | 'shimmer';
8
+ /**
9
+ * Props for `<Skeleton>` — a placeholder block that mimics the shape of
10
+ * content while it loads. Use to preserve layout and avoid layout shift
11
+ * when the real content arrives.
12
+ *
13
+ * For wholesale "is my component loading?" UX, most components expose
14
+ * a `loading` prop that renders skeletons automatically (`<Card loading>`,
15
+ * `<Table loading>`, etc.). Use `<Skeleton>` directly when composing
16
+ * custom placeholder layouts.
17
+ *
18
+ * @example
19
+ * ```svelte
20
+ * <!-- Three-line text placeholder -->
21
+ * <div class="space-y-2">
22
+ * <Skeleton class="h-4 w-48" />
23
+ * <Skeleton class="h-4 w-64" />
24
+ * <Skeleton class="h-4 w-40" />
25
+ * </div>
26
+ * ```
27
+ *
28
+ * @example
29
+ * ```svelte
30
+ * <!-- Avatar placeholder -->
31
+ * <Skeleton class="size-10" rounded="rounded-full" variant="shimmer" />
32
+ * ```
33
+ */
34
+ export interface SkeletonProps {
35
+ /** Additional Tailwind classes — typically `w-*` and `h-*` to size the block. */
36
+ class?: ClassValue;
37
+ /**
38
+ * Animation style.
39
+ * - `pulse` (default): subtle opacity pulse via Tailwind's animate-pulse
40
+ * - `shimmer`: gradient slides across the block
41
+ */
42
+ variant?: SkeletonVariant;
43
+ /** Override the rounded class. Pass `''` to suppress default rounding. @default 'rounded-md' */
44
+ rounded?: string;
45
+ /** Optional accessible label announced by screen readers. @default 'Loading' */
46
+ ariaLabel?: string;
47
+ /** Inline style — useful for dynamic widths/heights that Tailwind can't express. */
48
+ style?: string;
49
+ testId?: string;
50
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -6,7 +6,7 @@
6
6
  import type { SpinnerProps } from '../../index.js';
7
7
 
8
8
  let {
9
- size = Size.BASE,
9
+ size = Size.MD,
10
10
  color = Color.DEFAULT,
11
11
  label,
12
12
  class: className = '',
@@ -1,8 +1,28 @@
1
1
  import type { ClassValue } from 'tailwind-variants';
2
2
  import type { VariantColors, VariantSizes } from '../../index.js';
3
+ /**
4
+ * Props for `<Spinner>` — an animated loading indicator. Use for
5
+ * short-lived waits ("loading the next page of results"). For longer
6
+ * waits where layout shape matters, prefer `<Skeleton>`.
7
+ *
8
+ * @example
9
+ * ```svelte
10
+ * <Spinner />
11
+ *
12
+ * <!-- With accessible label -->
13
+ * <Spinner size="lg" color="primary" label="Loading users" />
14
+ * ```
15
+ */
3
16
  export type SpinnerProps = {
17
+ /** @default 'md' */
4
18
  size?: VariantSizes;
19
+ /** @default 'primary' */
5
20
  color?: VariantColors;
21
+ /**
22
+ * Accessible text announced to screen readers. Visible labels are
23
+ * rendered next to the spinner; omit for a bare spinner (an
24
+ * `aria-label='Loading'` is still applied).
25
+ */
6
26
  label?: string;
7
27
  class?: ClassValue;
8
28
  testId?: string;
@@ -8,7 +8,7 @@ export declare const spinner: import("tailwind-variants").TVReturnType<{
8
8
  svg: string;
9
9
  label: string;
10
10
  };
11
- base: {
11
+ md: {
12
12
  svg: string;
13
13
  label: string;
14
14
  };
@@ -62,7 +62,7 @@ export declare const spinner: import("tailwind-variants").TVReturnType<{
62
62
  svg: string;
63
63
  label: string;
64
64
  };
65
- base: {
65
+ md: {
66
66
  svg: string;
67
67
  label: string;
68
68
  };
@@ -116,7 +116,7 @@ export declare const spinner: import("tailwind-variants").TVReturnType<{
116
116
  svg: string;
117
117
  label: string;
118
118
  };
119
- base: {
119
+ md: {
120
120
  svg: string;
121
121
  label: string;
122
122
  };
@@ -10,7 +10,7 @@ export const spinner = tv({
10
10
  size: {
11
11
  [Size.XS]: { svg: 'h-3 w-3', label: 'text-xs' },
12
12
  [Size.SM]: { svg: 'h-4 w-4', label: 'text-xs' },
13
- [Size.BASE]: { svg: 'h-5 w-5', label: 'text-sm' },
13
+ [Size.MD]: { svg: 'h-5 w-5', label: 'text-sm' },
14
14
  [Size.LG]: { svg: 'h-6 w-6', label: 'text-base' },
15
15
  [Size.XL]: { svg: 'h-8 w-8', label: 'text-lg' },
16
16
  [Size.XXL]: { svg: 'h-12 w-12', label: 'text-xl' }
@@ -26,7 +26,7 @@ export const spinner = tv({
26
26
  }
27
27
  },
28
28
  defaultVariants: {
29
- size: Size.BASE,
29
+ size: Size.MD,
30
30
  color: Color.DEFAULT
31
31
  }
32
32
  });
@@ -1,12 +1,19 @@
1
1
  <script lang="ts">
2
2
  import { cn } from '../../helper/cls.js';
3
3
  import { buildTestId } from '../../helper/testid.js';
4
- import type { TooltipProps } from '../../index.js';
4
+ import { scale } from 'svelte/transition';
5
+ import { quintOut } from 'svelte/easing';
6
+ import type { TooltipProps, TooltipSize, TooltipVariant } from '../../index.js';
5
7
 
6
8
  let {
7
9
  content,
10
+ body,
8
11
  placement = 'top',
12
+ size = 'sm',
13
+ variant = 'dark',
14
+ arrow = true,
9
15
  delay = 100,
16
+ hideDelay = 0,
10
17
  disabled = false,
11
18
  class: className = '',
12
19
  tooltipClass = '',
@@ -16,10 +23,12 @@
16
23
 
17
24
  let visible = $state(false);
18
25
  let showTimer: ReturnType<typeof setTimeout> | undefined;
26
+ let hideTimer: ReturnType<typeof setTimeout> | undefined;
19
27
 
20
28
  function show() {
21
29
  if (disabled) return;
22
30
  clearTimeout(showTimer);
31
+ clearTimeout(hideTimer);
23
32
  if (delay > 0) {
24
33
  showTimer = setTimeout(() => {
25
34
  visible = true;
@@ -31,7 +40,19 @@
31
40
 
32
41
  function hide() {
33
42
  clearTimeout(showTimer);
34
- visible = false;
43
+ clearTimeout(hideTimer);
44
+ if (hideDelay > 0) {
45
+ hideTimer = setTimeout(() => {
46
+ visible = false;
47
+ }, hideDelay);
48
+ } else {
49
+ visible = false;
50
+ }
51
+ }
52
+
53
+ /** Keep tooltip open while the user mouses over its body (only meaningful with hideDelay > 0). */
54
+ function cancelHide() {
55
+ if (hideDelay > 0) clearTimeout(hideTimer);
35
56
  }
36
57
 
37
58
  const placementClass = $derived(
@@ -43,16 +64,78 @@
43
64
  }[placement]
44
65
  );
45
66
 
67
+ const sizeClasses: Record<TooltipSize, string> = {
68
+ sm: 'px-2 py-1 text-xs rounded',
69
+ md: 'px-3 py-1.5 text-sm rounded-md',
70
+ lg: 'px-4 py-2 text-base rounded-lg'
71
+ };
72
+
73
+ type VariantStyles = {
74
+ bg: string;
75
+ text: string;
76
+ arrow: { t: string; b: string; l: string; r: string };
77
+ };
78
+
79
+ const variantStyles: Record<TooltipVariant, VariantStyles> = {
80
+ dark: {
81
+ bg: 'bg-default-800',
82
+ text: 'text-white',
83
+ arrow: {
84
+ t: 'border-t-default-800',
85
+ b: 'border-b-default-800',
86
+ l: 'border-l-default-800',
87
+ r: 'border-r-default-800'
88
+ }
89
+ },
90
+ light: {
91
+ bg: 'bg-white border border-default-200',
92
+ text: 'text-default-900',
93
+ arrow: { t: 'border-t-white', b: 'border-b-white', l: 'border-l-white', r: 'border-r-white' }
94
+ },
95
+ primary: {
96
+ bg: 'bg-primary-600',
97
+ text: 'text-white',
98
+ arrow: {
99
+ t: 'border-t-primary-600',
100
+ b: 'border-b-primary-600',
101
+ l: 'border-l-primary-600',
102
+ r: 'border-r-primary-600'
103
+ }
104
+ },
105
+ danger: {
106
+ bg: 'bg-danger-600',
107
+ text: 'text-white',
108
+ arrow: {
109
+ t: 'border-t-danger-600',
110
+ b: 'border-b-danger-600',
111
+ l: 'border-l-danger-600',
112
+ r: 'border-r-danger-600'
113
+ }
114
+ },
115
+ success: {
116
+ bg: 'bg-success-600',
117
+ text: 'text-white',
118
+ arrow: {
119
+ t: 'border-t-success-600',
120
+ b: 'border-b-success-600',
121
+ l: 'border-l-success-600',
122
+ r: 'border-r-success-600'
123
+ }
124
+ }
125
+ };
126
+
127
+ const styles = $derived(variantStyles[variant]);
128
+
46
129
  const arrowClass = $derived(
47
130
  {
48
- top: 'top-full left-1/2 -translate-x-1/2 border-t-default-800 border-l-transparent border-r-transparent border-b-transparent',
49
- bottom:
50
- 'bottom-full left-1/2 -translate-x-1/2 border-b-default-800 border-l-transparent border-r-transparent border-t-transparent',
51
- left: 'left-full top-1/2 -translate-y-1/2 border-l-default-800 border-t-transparent border-b-transparent border-r-transparent',
52
- right:
53
- 'right-full top-1/2 -translate-y-1/2 border-r-default-800 border-t-transparent border-b-transparent border-l-transparent'
131
+ top: `top-full left-1/2 -translate-x-1/2 ${styles.arrow.t} border-l-transparent border-r-transparent border-b-transparent`,
132
+ bottom: `bottom-full left-1/2 -translate-x-1/2 ${styles.arrow.b} border-l-transparent border-r-transparent border-t-transparent`,
133
+ left: `left-full top-1/2 -translate-y-1/2 ${styles.arrow.l} border-t-transparent border-b-transparent border-r-transparent`,
134
+ right: `right-full top-1/2 -translate-y-1/2 ${styles.arrow.r} border-t-transparent border-b-transparent border-l-transparent`
54
135
  }[placement]
55
136
  );
137
+
138
+ const bodyIsInteractive = $derived(hideDelay > 0);
56
139
  </script>
57
140
 
58
141
  <span
@@ -69,14 +152,28 @@
69
152
  <span
70
153
  role="tooltip"
71
154
  data-tooltip-visible="true"
155
+ transition:scale={{ duration: 500, start: 0.9, opacity: 0, easing: quintOut }}
72
156
  class={cn(
73
- 'bg-default-800 pointer-events-none absolute z-50 rounded px-2 py-1 text-xs whitespace-nowrap text-white shadow-lg',
157
+ 'absolute z-50 shadow-lg',
158
+ !bodyIsInteractive && 'pointer-events-none',
159
+ body ? '' : 'whitespace-nowrap',
160
+ sizeClasses[size],
161
+ styles.bg,
162
+ styles.text,
74
163
  placementClass,
75
164
  tooltipClass
76
165
  )}
166
+ onmouseenter={cancelHide}
167
+ onmouseleave={hide}
77
168
  >
78
- {content}
79
- <span class={cn('absolute h-0 w-0 border-4', arrowClass)} aria-hidden="true"></span>
169
+ {#if body}
170
+ {@render body()}
171
+ {:else}
172
+ {content}
173
+ {/if}
174
+ {#if arrow}
175
+ <span class={cn('absolute h-0 w-0 border-4', arrowClass)} aria-hidden="true"></span>
176
+ {/if}
80
177
  </span>
81
178
  {/if}
82
179
  </span>
@@ -1,13 +1,61 @@
1
1
  import type { ClassValue } from 'tailwind-variants';
2
2
  import type { Snippet } from 'svelte';
3
+ /** Placement of the tooltip panel relative to its trigger. */
3
4
  export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
5
+ /** Size variants specific to Tooltip — a narrower subset of the global `VariantSizes`. */
6
+ export type TooltipSize = 'sm' | 'md' | 'lg';
7
+ /** Tooltip color palette. `'dark'` on light pages, `'light'` on dark pages. */
8
+ export type TooltipVariant = 'dark' | 'light' | 'primary' | 'danger' | 'success';
9
+ /**
10
+ * Props for `<Tooltip>` — small hover/focus-triggered label for a UI
11
+ * element. For longer or interactive content (links, buttons inside
12
+ * the panel), prefer `<Popover trigger="hover" hideDelay={200}>` —
13
+ * Tooltip closes immediately on mouseleave by default.
14
+ *
15
+ * @example
16
+ * ```svelte
17
+ * <Tooltip content="Delete this user">
18
+ * <Button variant="ghost" color="danger"><TrashIcon /></Button>
19
+ * </Tooltip>
20
+ * ```
21
+ *
22
+ * @example
23
+ * ```svelte
24
+ * <!-- Rich content + interactive (keeps open while mouse is over body) -->
25
+ * <Tooltip variant="light" placement="right" hideDelay={200}>
26
+ * {#snippet body()}
27
+ * <div>
28
+ * <strong>Keyboard shortcut</strong>
29
+ * <p class="text-xs">Press ⌘ + K to open search</p>
30
+ * </div>
31
+ * {/snippet}
32
+ * <kbd>⌘ K</kbd>
33
+ * </Tooltip>
34
+ * ```
35
+ */
4
36
  export type TooltipProps = {
5
- content: string;
37
+ /** Plain text content. Ignored if `body` snippet is also provided. */
38
+ content?: string;
39
+ /** Rich content snippet — takes precedence over `content`. Use for HTML, links, icons, etc. */
40
+ body?: Snippet;
41
+ /** @default 'top' */
6
42
  placement?: TooltipPlacement;
43
+ /** Padding/text size. @default 'sm' */
44
+ size?: TooltipSize;
45
+ /** Color palette. @default 'dark' */
46
+ variant?: TooltipVariant;
47
+ /** Show arrow pointing at the trigger. @default true */
48
+ arrow?: boolean;
49
+ /** Delay before showing (ms). @default 100 */
7
50
  delay?: number;
51
+ /** Delay before hiding after mouseleave (ms). Non-zero lets users move into the tooltip. @default 0 */
52
+ hideDelay?: number;
8
53
  disabled?: boolean;
54
+ /** Classes for the trigger wrapper. */
9
55
  class?: ClassValue;
56
+ /** Classes for the tooltip panel. */
10
57
  tooltipClass?: ClassValue;
58
+ /** Trigger element(s). */
11
59
  children: Snippet;
12
60
  testId?: string;
13
61
  };
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte';
3
3
  import { cn } from '../helper/cls.js';
4
- import { Button, Table, Color, Size, Spinner } from '../index.js';
4
+ import { Button, Table, Color, Size, Spinner, Skeleton } from '../index.js';
5
5
  import type { TableColumn, FileBrowserProps } from '../index.js';
6
6
  import { formatDate } from '../utils/dateUtils.js';
7
7
  import type {
@@ -27,7 +27,7 @@
27
27
  let files = $state<FileItem[]>([]);
28
28
  let displayFiles = $state<FileItem[]>([]);
29
29
  let currentPath = $state(startPath || '');
30
- let isLoading = $state(true);
30
+ let loading = $state(true);
31
31
  let isAuthenticated = $state(false);
32
32
  let error = $state<string | null>(null);
33
33
  let breadcrumbs = $state<Breadcrumb[]>([]);
@@ -162,7 +162,7 @@
162
162
 
163
163
  // Loading files using the adapter
164
164
  async function listFiles(path: string) {
165
- isLoading = true;
165
+ loading = true;
166
166
  error = null;
167
167
 
168
168
  // Remember the previous selection before loading new files
@@ -192,7 +192,7 @@
192
192
  console.error('Error fetching files:', err);
193
193
  error = err instanceof Error ? err.message : 'An unknown error occurred';
194
194
  } finally {
195
- isLoading = false;
195
+ loading = false;
196
196
  }
197
197
  }
198
198
 
@@ -647,7 +647,7 @@
647
647
  ];
648
648
 
649
649
  async function handleUnauthenticated() {
650
- isLoading = true;
650
+ loading = true;
651
651
 
652
652
  try {
653
653
  // Use the adapter's method to set the reopening flag
@@ -664,11 +664,11 @@
664
664
  }
665
665
 
666
666
  // Note: authentication usually redirects the page, so code below won't run
667
- isLoading = false;
667
+ loading = false;
668
668
  } catch (error) {
669
669
  console.error('Authentication error:', error);
670
670
  console.error('Authentication error');
671
- isLoading = false;
671
+ loading = false;
672
672
  }
673
673
  }
674
674
 
@@ -914,9 +914,18 @@
914
914
  </div>
915
915
 
916
916
  <div class="min-h-0 flex-1 overflow-auto">
917
- {#if isLoading}
918
- <div class="flex h-full items-center justify-center py-16">
919
- <div class="text-default-500">Loading files...</div>
917
+ {#if loading}
918
+ <div class="divide-default-100 divide-y px-4" aria-label="Loading files">
919
+ {#each Array(6) as _, i (i)}
920
+ <div class="flex items-center gap-3 py-3">
921
+ <Skeleton class="size-5 shrink-0" />
922
+ <Skeleton class="size-6 shrink-0" rounded="rounded-md" />
923
+ <Skeleton class="h-4 flex-1" style="max-width: {200 + i * 30}px" />
924
+ <Skeleton class="h-3 w-16 shrink-0" />
925
+ <Skeleton class="h-3 w-24 shrink-0" />
926
+ <Skeleton class="h-7 w-16 shrink-0" rounded="rounded-md" />
927
+ </div>
928
+ {/each}
920
929
  </div>
921
930
  {:else if error}
922
931
  <div class="flex h-full flex-col items-center justify-center py-16">
@@ -931,10 +940,10 @@
931
940
  <Table
932
941
  {columns}
933
942
  data={displayFiles}
934
- loading={isLoading}
943
+ {loading}
935
944
  bordered={false}
936
945
  onrowclick={handleRowClick}
937
- rowclass={(row) => {
946
+ rowClass={(row: FileItem) => {
938
947
  let classes = row.isFolder ? 'hover:bg-amber-50 cursor-pointer' : 'hover:bg-blue-50';
939
948
  if (isRowSelected(row)) {
940
949
  classes += ' bg-primary-50';