@mrintel/villain-ui 0.3.0 → 0.7.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 (140) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +3490 -1296
  3. package/dist/components/buttons/Button.svelte +27 -33
  4. package/dist/components/buttons/Button.svelte.d.ts +4 -1
  5. package/dist/components/buttons/ButtonGroup.svelte +17 -30
  6. package/dist/components/buttons/FloatingActionButton.svelte +20 -44
  7. package/dist/components/buttons/FloatingActionButton.svelte.d.ts +2 -1
  8. package/dist/components/buttons/IconButton.svelte +23 -53
  9. package/dist/components/buttons/IconButton.svelte.d.ts +2 -1
  10. package/dist/components/buttons/LinkButton.svelte +24 -37
  11. package/dist/components/buttons/LinkButton.svelte.d.ts +4 -1
  12. package/dist/components/buttons/buttonClasses.d.ts +5 -0
  13. package/dist/components/buttons/buttonClasses.js +8 -3
  14. package/dist/components/cards/Card.svelte +54 -46
  15. package/dist/components/cards/Card.svelte.d.ts +9 -2
  16. package/dist/components/cards/Container.svelte +17 -33
  17. package/dist/components/cards/Divider.svelte +36 -52
  18. package/dist/components/cards/Divider.svelte.d.ts +2 -0
  19. package/dist/components/cards/Grid.svelte +55 -44
  20. package/dist/components/cards/Panel.svelte +18 -32
  21. package/dist/components/cards/Panel.svelte.d.ts +2 -1
  22. package/dist/components/cards/SectionHeader.svelte +24 -38
  23. package/dist/components/cards/SectionHeader.svelte.d.ts +1 -0
  24. package/dist/components/data/Avatar.svelte +48 -67
  25. package/dist/components/data/Badge.svelte +45 -32
  26. package/dist/components/data/Badge.svelte.d.ts +7 -1
  27. package/dist/components/data/CalendarGrid.svelte +433 -0
  28. package/dist/components/data/CalendarGrid.svelte.d.ts +25 -0
  29. package/dist/components/data/CalendarGrid.types.d.ts +7 -0
  30. package/dist/components/data/CalendarGrid.types.js +1 -0
  31. package/dist/components/data/CodeBlock.svelte +119 -121
  32. package/dist/components/data/CodeBlock.svelte.d.ts +8 -0
  33. package/dist/components/data/List.svelte +87 -64
  34. package/dist/components/data/List.svelte.d.ts +7 -0
  35. package/dist/components/data/Pagination.svelte +121 -123
  36. package/dist/components/data/Pagination.svelte.d.ts +5 -0
  37. package/dist/components/data/Sparkline.svelte +117 -0
  38. package/dist/components/data/Sparkline.svelte.d.ts +43 -0
  39. package/dist/components/data/Stat.svelte +92 -103
  40. package/dist/components/data/Table.svelte +443 -76
  41. package/dist/components/data/Table.svelte.d.ts +23 -2
  42. package/dist/components/data/Table.types.d.ts +14 -0
  43. package/dist/components/data/Table.types.js +1 -0
  44. package/dist/components/data/Tag.svelte +51 -53
  45. package/dist/components/data/Tag.svelte.d.ts +5 -1
  46. package/dist/components/data/index.d.ts +4 -0
  47. package/dist/components/data/index.js +2 -0
  48. package/dist/components/forms/Checkbox.svelte +39 -51
  49. package/dist/components/forms/Checkbox.svelte.d.ts +3 -1
  50. package/dist/components/forms/DatePicker.svelte +61 -0
  51. package/dist/components/forms/DatePicker.svelte.d.ts +15 -0
  52. package/dist/components/forms/DateTimePicker.svelte +63 -0
  53. package/dist/components/forms/DateTimePicker.svelte.d.ts +16 -0
  54. package/dist/components/forms/FileUpload.svelte +136 -164
  55. package/dist/components/forms/FileUpload.svelte.d.ts +1 -0
  56. package/dist/components/forms/Input.svelte +284 -57
  57. package/dist/components/forms/Input.svelte.d.ts +10 -3
  58. package/dist/components/forms/InputGroup.svelte +7 -7
  59. package/dist/components/forms/RadioGroup.svelte +77 -87
  60. package/dist/components/forms/RadioGroup.svelte.d.ts +3 -1
  61. package/dist/components/forms/RangeSlider.svelte +90 -116
  62. package/dist/components/forms/Select.svelte +106 -71
  63. package/dist/components/forms/Select.svelte.d.ts +3 -1
  64. package/dist/components/forms/Step.svelte +25 -0
  65. package/dist/components/forms/Step.svelte.d.ts +12 -0
  66. package/dist/components/forms/StepContext.d.ts +3 -0
  67. package/dist/components/forms/StepContext.js +5 -0
  68. package/dist/components/forms/Stepper.types.d.ts +37 -0
  69. package/dist/components/forms/Stepper.types.js +1 -0
  70. package/dist/components/forms/StepperForm.svelte +183 -0
  71. package/dist/components/forms/StepperForm.svelte.d.ts +17 -0
  72. package/dist/components/forms/Switch.svelte +44 -56
  73. package/dist/components/forms/Switch.svelte.d.ts +3 -1
  74. package/dist/components/forms/Textarea.svelte +52 -57
  75. package/dist/components/forms/Textarea.svelte.d.ts +3 -1
  76. package/dist/components/forms/TimePicker.svelte +63 -0
  77. package/dist/components/forms/TimePicker.svelte.d.ts +16 -0
  78. package/dist/components/forms/formClasses.d.ts +3 -0
  79. package/dist/components/forms/formClasses.js +3 -0
  80. package/dist/components/forms/index.d.ts +6 -0
  81. package/dist/components/forms/index.js +5 -0
  82. package/dist/components/navigation/Breadcrumbs.svelte +56 -59
  83. package/dist/components/navigation/Breadcrumbs.svelte.d.ts +1 -0
  84. package/dist/components/navigation/ContextMenu.svelte +133 -83
  85. package/dist/components/navigation/ContextMenu.svelte.d.ts +8 -1
  86. package/dist/components/navigation/DropdownMenu.svelte +139 -80
  87. package/dist/components/navigation/DropdownMenu.svelte.d.ts +8 -1
  88. package/dist/components/navigation/Menu.svelte +72 -48
  89. package/dist/components/navigation/Navbar.svelte +111 -32
  90. package/dist/components/navigation/Navbar.svelte.d.ts +6 -0
  91. package/dist/components/navigation/Sidebar.svelte +236 -35
  92. package/dist/components/navigation/Sidebar.svelte.d.ts +2 -0
  93. package/dist/components/navigation/Stepper.svelte +204 -0
  94. package/dist/components/navigation/Stepper.svelte.d.ts +34 -0
  95. package/dist/components/navigation/Tabs.svelte +86 -54
  96. package/dist/components/navigation/Tabs.svelte.d.ts +5 -1
  97. package/dist/components/navigation/index.d.ts +1 -0
  98. package/dist/components/navigation/index.js +1 -0
  99. package/dist/components/overlays/Alert.svelte +81 -99
  100. package/dist/components/overlays/Alert.svelte.d.ts +5 -1
  101. package/dist/components/overlays/CommandPalette.svelte +182 -217
  102. package/dist/components/overlays/Drawer.svelte +158 -167
  103. package/dist/components/overlays/Drawer.svelte.d.ts +3 -1
  104. package/dist/components/overlays/Dropdown.svelte +62 -30
  105. package/dist/components/overlays/Dropdown.svelte.d.ts +2 -0
  106. package/dist/components/overlays/Modal.svelte +125 -130
  107. package/dist/components/overlays/Modal.svelte.d.ts +3 -1
  108. package/dist/components/overlays/Popover.svelte +106 -131
  109. package/dist/components/overlays/ProgressBar.svelte +29 -45
  110. package/dist/components/overlays/SkeletonLoader.svelte +66 -82
  111. package/dist/components/overlays/Spinner.svelte +33 -43
  112. package/dist/components/overlays/Toast.svelte +111 -140
  113. package/dist/components/overlays/Toast.svelte.d.ts +3 -0
  114. package/dist/components/overlays/Tooltip.svelte +94 -115
  115. package/dist/components/overlays/Tooltip.svelte.d.ts +3 -1
  116. package/dist/components/typography/Code.svelte +10 -14
  117. package/dist/components/typography/Heading.svelte +15 -22
  118. package/dist/components/typography/Heading.svelte.d.ts +1 -0
  119. package/dist/components/typography/Text.svelte +21 -24
  120. package/dist/components/typography/Text.svelte.d.ts +2 -1
  121. package/dist/components/utilities/Accordion.svelte +54 -67
  122. package/dist/components/utilities/Accordion.svelte.d.ts +4 -1
  123. package/dist/components/utilities/Carousel.svelte +124 -152
  124. package/dist/components/utilities/Collapse.svelte +46 -60
  125. package/dist/components/utilities/Hero.svelte +42 -0
  126. package/dist/components/utilities/Hero.svelte.d.ts +10 -0
  127. package/dist/components/utilities/Portal.svelte +47 -72
  128. package/dist/components/utilities/ScrollArea.svelte +33 -41
  129. package/dist/components/utilities/SystemConsole.svelte +310 -0
  130. package/dist/components/utilities/SystemConsole.svelte.d.ts +20 -0
  131. package/dist/components/utilities/SystemInterface.svelte +726 -0
  132. package/dist/components/utilities/SystemInterface.svelte.d.ts +19 -0
  133. package/dist/components/utilities/index.d.ts +4 -0
  134. package/dist/components/utilities/index.js +3 -0
  135. package/dist/components/utilities/utilities.types.d.ts +46 -0
  136. package/dist/components/utilities/utilities.types.js +4 -0
  137. package/dist/index.d.ts +57 -5
  138. package/dist/index.js +5 -5
  139. package/dist/theme.css +2889 -218
  140. package/package.json +83 -76
@@ -0,0 +1,17 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ValidationMode, StepperFormContext } from './Stepper.types';
3
+ interface Props {
4
+ form: any;
5
+ validationMode?: ValidationMode;
6
+ initialStep?: number;
7
+ onStepChange?: (stepIndex: number, stepId: string) => void;
8
+ onValidationError?: (stepId: string, errors: string[]) => void;
9
+ onComplete?: () => void;
10
+ header?: Snippet<[StepperFormContext]>;
11
+ footer?: Snippet<[StepperFormContext]>;
12
+ children?: Snippet<[StepperFormContext]>;
13
+ class?: string;
14
+ }
15
+ declare const StepperForm: import("svelte").Component<Props, {}, "">;
16
+ type StepperForm = ReturnType<typeof StepperForm>;
17
+ export default StepperForm;
@@ -1,56 +1,44 @@
1
- <script lang="ts">
2
- import { createId } from '../../lib/internal/id.js';
3
-
4
- interface Props {
5
- checked?: boolean;
6
- disabled?: boolean;
7
- label?: string;
8
- id?: string;
9
- onchange?: (event: Event) => void;
10
- }
11
-
12
- let {
13
- checked = $bindable(false),
14
- disabled = false,
15
- label,
16
- id = createId('switch'),
17
- onchange
18
- }: Props = $props();
19
- </script>
20
-
21
- <label for={id} class="flex items-center gap-2 cursor-pointer {disabled ? 'opacity-50 cursor-not-allowed' : ''}">
22
- <input
23
- type="checkbox"
24
- role="switch"
25
- aria-checked={checked}
26
- {id}
27
- {disabled}
28
- bind:checked
29
- onchange={onchange}
30
- class="w-11 h-6 rounded-pill bg-base-3 border border-border appearance-none transition-all duration-300 ease-luxe cursor-pointer checked:bg-accent checked:accent-glow focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 focus:ring-offset-base-1 relative {disabled ? 'cursor-not-allowed' : ''}"
31
- />
32
- {#if label}
33
- <span class="text-text text-sm select-none">
34
- {label}
35
- </span>
36
- {/if}
37
- </label>
38
-
39
- <style>
40
- input[type="checkbox"]::after {
41
- content: '';
42
- position: absolute;
43
- left: 0.25rem;
44
- top: 50%;
45
- transform: translateY(-50%);
46
- width: 1rem;
47
- height: 1rem;
48
- border-radius: var(--radius-pill);
49
- background: var(--color-text);
50
- transition: all 0.3s var(--ease-luxe);
51
- }
52
-
53
- input[type="checkbox"]:checked::after {
54
- transform: translate(1.25rem, -50%);
55
- }
56
- </style>
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ let { checked = $bindable(false), disabled = false, label, id = createId('switch'), onchange, iconBefore, class: className = '' } = $props();
3
+ </script>
4
+
5
+ <label for={id} class="flex items-center gap-2 cursor-pointer {disabled ? 'opacity-50 cursor-not-allowed' : ''} {className}">
6
+ <input
7
+ type="checkbox"
8
+ role="switch"
9
+ aria-checked={checked}
10
+ {id}
11
+ {disabled}
12
+ bind:checked
13
+ onchange={onchange}
14
+ class="w-14 h-7 rounded-pill bg-base-3 border border-border appearance-none transition-all duration-300 ease-luxe cursor-pointer checked:bg-accent checked:accent-glow focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 focus:ring-offset-base-1 relative {disabled ? 'cursor-not-allowed' : ''}"
15
+ />
16
+ {#if iconBefore}
17
+ <span class="inline-flex items-center justify-center text-text-soft">
18
+ {@render iconBefore()}
19
+ </span>
20
+ {/if}
21
+ {#if label}
22
+ <span class="text-text text-sm select-none">
23
+ {label}
24
+ </span>
25
+ {/if}
26
+ </label>
27
+
28
+ <style>
29
+ input[type="checkbox"]::after {
30
+ content: '';
31
+ position: absolute;
32
+ left: 0.375rem;
33
+ top: 50%;
34
+ transform: translateY(-50%);
35
+ width: 1.25rem;
36
+ height: 1.25rem;
37
+ border-radius: var(--radius-pill);
38
+ background: var(--color-text);
39
+ transition: all 0.3s var(--ease-luxe);
40
+ }
41
+
42
+ input[type="checkbox"]:checked::after {
43
+ transform: translate(1.75rem, -50%);
44
+ }</style>
@@ -1,9 +1,11 @@
1
- interface Props {
1
+ export interface Props {
2
2
  checked?: boolean;
3
3
  disabled?: boolean;
4
4
  label?: string;
5
5
  id?: string;
6
6
  onchange?: (event: Event) => void;
7
+ iconBefore?: import('svelte').Snippet;
8
+ class?: string;
7
9
  }
8
10
  declare const Switch: import("svelte").Component<Props, {}, "checked">;
9
11
  type Switch = ReturnType<typeof Switch>;
@@ -1,57 +1,52 @@
1
- <script lang="ts">
2
- import { createId } from '../../lib/internal/id.js';
3
-
4
- interface Props {
5
- value?: string;
6
- placeholder?: string;
7
- rows?: number;
8
- disabled?: boolean;
9
- error?: boolean;
10
- label?: string;
11
- id?: string;
12
- oninput?: (event: Event) => void;
13
- }
14
-
15
- let {
16
- value = $bindable(''),
17
- placeholder,
18
- rows = 4,
19
- disabled = false,
20
- error = false,
21
- label,
22
- id = createId('textarea'),
23
- oninput
24
- }: Props = $props();
25
-
26
- const baseClasses = 'glass-panel rounded-lg px-4 py-3 font-body text-text placeholder:text-text-muted transition-all duration-300 ease-luxe w-full resize-y min-h-[100px]';
27
- const focusClasses = 'focus:outline-none focus:border-accent focus:accent-glow';
28
- const errorClasses = error ? 'border-error' : '';
29
- const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : '';
30
- </script>
31
-
32
- {#if label}
33
- <div>
34
- <label for={id} class="text-text-soft text-sm mb-2 block">
35
- {label}
36
- </label>
37
- <textarea
38
- {id}
39
- {placeholder}
40
- {disabled}
41
- {rows}
42
- bind:value
43
- oninput={oninput}
44
- class="{baseClasses} {focusClasses} {errorClasses} {disabledClasses}"
45
- ></textarea>
46
- </div>
47
- {:else}
48
- <textarea
49
- {id}
50
- {placeholder}
51
- {disabled}
52
- {rows}
53
- bind:value
54
- oninput={oninput}
55
- class="{baseClasses} {focusClasses} {errorClasses} {disabledClasses}"
56
- ></textarea>
57
- {/if}
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ import { baseInputClasses, focusClasses, disabledClasses } from './formClasses';
3
+ let { value = $bindable(''), placeholder, rows = 4, disabled = false, error = false, label, id = createId('textarea'), oninput, iconBefore, class: className = '' } = $props();
4
+ // Icon spacing: pl-12 (3rem) = left-4 (1rem) icon position + ~2rem for icon width and spacing
5
+ // top-4 keeps icon fixed at top when textarea is resized
6
+ // This ensures text doesn't overlap with the absolutely positioned icon
7
+ const textareaClasses = `${baseInputClasses} resize-y min-h-[100px]`;
8
+ const errorClasses = $derived(error ? 'error-state' : '');
9
+ </script>
10
+
11
+ {#if label}
12
+ <div>
13
+ <label for={id} class="text-text-soft text-sm mb-2 block">
14
+ {label}
15
+ </label>
16
+ <div class="relative">
17
+ {#if iconBefore}
18
+ <span class="absolute left-4 top-4 z-10 inline-flex items-center justify-center text-text-soft pointer-events-none">
19
+ {@render iconBefore()}
20
+ </span>
21
+ {/if}
22
+ <textarea
23
+ {id}
24
+ {placeholder}
25
+ {disabled}
26
+ {rows}
27
+ bind:value
28
+ oninput={oninput}
29
+ class="{textareaClasses} {focusClasses} {errorClasses} {disabled ? disabledClasses : ''} {className}"
30
+ class:pl-12={iconBefore}
31
+ ></textarea>
32
+ </div>
33
+ </div>
34
+ {:else}
35
+ <div class="relative">
36
+ {#if iconBefore}
37
+ <span class="absolute left-4 top-4 z-10 inline-flex items-center justify-center text-text-soft pointer-events-none">
38
+ {@render iconBefore()}
39
+ </span>
40
+ {/if}
41
+ <textarea
42
+ {id}
43
+ {placeholder}
44
+ {disabled}
45
+ {rows}
46
+ bind:value
47
+ oninput={oninput}
48
+ class="{textareaClasses} {focusClasses} {errorClasses} {disabled ? disabledClasses : ''} {className}"
49
+ class:pl-12={iconBefore}
50
+ ></textarea>
51
+ </div>
52
+ {/if}
@@ -1,4 +1,4 @@
1
- interface Props {
1
+ export interface Props {
2
2
  value?: string;
3
3
  placeholder?: string;
4
4
  rows?: number;
@@ -7,6 +7,8 @@ interface Props {
7
7
  label?: string;
8
8
  id?: string;
9
9
  oninput?: (event: Event) => void;
10
+ iconBefore?: import('svelte').Snippet;
11
+ class?: string;
10
12
  }
11
13
  declare const Textarea: import("svelte").Component<Props, {}, "value">;
12
14
  type Textarea = ReturnType<typeof Textarea>;
@@ -0,0 +1,63 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ import { baseInputClasses, focusClasses, disabledClasses, } from './formClasses';
3
+ let { value = $bindable(''), min, max, step, placeholder, disabled = false, error = false, label, id = createId('timepicker'), onchange, class: className = '', } = $props();
4
+ const errorClasses = $derived(error ? 'error-state' : '');
5
+ </script>
6
+
7
+ {#if label}
8
+ <div>
9
+ <label for={id} class="text-text-soft text-sm mb-2 block">
10
+ {label}
11
+ </label>
12
+ <input
13
+ type="time"
14
+ {id}
15
+ {placeholder}
16
+ {disabled}
17
+ {min}
18
+ {max}
19
+ {step}
20
+ bind:value
21
+ {onchange}
22
+ class="{baseInputClasses} {focusClasses} {errorClasses} {disabled
23
+ ? disabledClasses
24
+ : ''} {className}"
25
+ />
26
+ </div>
27
+ {:else}
28
+ <input
29
+ type="time"
30
+ {id}
31
+ {placeholder}
32
+ {disabled}
33
+ {min}
34
+ {max}
35
+ {step}
36
+ bind:value
37
+ {onchange}
38
+ class="{baseInputClasses} {focusClasses} {errorClasses} {disabled
39
+ ? disabledClasses
40
+ : ''} {className}"
41
+ />
42
+ {/if}
43
+
44
+ <style>
45
+ input[type='time'] {
46
+ min-height: 3rem;
47
+ cursor: pointer;
48
+ }
49
+
50
+ input[type='time']::-webkit-calendar-picker-indicator {
51
+ cursor: pointer;
52
+ filter: invert(0.7);
53
+ transition: filter 120ms var(--ease-sharp);
54
+ }
55
+
56
+ input[type='time']:hover::-webkit-calendar-picker-indicator {
57
+ filter: invert(1);
58
+ }
59
+
60
+ input[type='time']:disabled::-webkit-calendar-picker-indicator {
61
+ cursor: not-allowed;
62
+ opacity: 0.4;
63
+ }</style>
@@ -0,0 +1,16 @@
1
+ export interface Props {
2
+ value?: string;
3
+ min?: string;
4
+ max?: string;
5
+ step?: number;
6
+ placeholder?: string;
7
+ disabled?: boolean;
8
+ error?: boolean;
9
+ label?: string;
10
+ id?: string;
11
+ onchange?: (event: Event) => void;
12
+ class?: string;
13
+ }
14
+ declare const TimePicker: import("svelte").Component<Props, {}, "value">;
15
+ type TimePicker = ReturnType<typeof TimePicker>;
16
+ export default TimePicker;
@@ -0,0 +1,3 @@
1
+ export declare const baseInputClasses = "glass-panel rounded-[var(--radius-lg)] px-5 py-3.5 font-body text-text placeholder:text-text-muted transition-all duration-300 ease-luxe w-full";
2
+ export declare const focusClasses = "focus:outline-none focus:border-accent focus:accent-glow";
3
+ export declare const disabledClasses = "opacity-50 cursor-not-allowed";
@@ -0,0 +1,3 @@
1
+ export const baseInputClasses = 'glass-panel rounded-[var(--radius-lg)] px-5 py-3.5 font-body text-text placeholder:text-text-muted transition-all duration-300 ease-luxe w-full';
2
+ export const focusClasses = 'focus:outline-none focus:border-accent focus:accent-glow';
3
+ export const disabledClasses = 'opacity-50 cursor-not-allowed';
@@ -7,3 +7,9 @@ export { default as RadioGroup } from './RadioGroup.svelte';
7
7
  export { default as RangeSlider } from './RangeSlider.svelte';
8
8
  export { default as FileUpload } from './FileUpload.svelte';
9
9
  export { default as InputGroup } from './InputGroup.svelte';
10
+ export { default as DatePicker } from './DatePicker.svelte';
11
+ export { default as TimePicker } from './TimePicker.svelte';
12
+ export { default as DateTimePicker } from './DateTimePicker.svelte';
13
+ export { default as Step } from './Step.svelte';
14
+ export { default as StepperForm } from './StepperForm.svelte';
15
+ export type { StepState, ValidationMode, StepRegistration, StepperFormContext, StepMeta, StepContext } from './Stepper.types';
@@ -7,3 +7,8 @@ export { default as RadioGroup } from './RadioGroup.svelte';
7
7
  export { default as RangeSlider } from './RangeSlider.svelte';
8
8
  export { default as FileUpload } from './FileUpload.svelte';
9
9
  export { default as InputGroup } from './InputGroup.svelte';
10
+ export { default as DatePicker } from './DatePicker.svelte';
11
+ export { default as TimePicker } from './TimePicker.svelte';
12
+ export { default as DateTimePicker } from './DateTimePicker.svelte';
13
+ export { default as Step } from './Step.svelte';
14
+ export { default as StepperForm } from './StepperForm.svelte';
@@ -1,59 +1,56 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
-
4
- interface BreadcrumbItem {
5
- label: string;
6
- href?: string;
7
- onclick?: () => void;
8
- }
9
-
10
- interface Props {
11
- items: BreadcrumbItem[];
12
- separator?: string;
13
- separatorIcon?: Snippet;
14
- }
15
-
16
- let {
17
- items,
18
- separator = '/',
19
- separatorIcon
20
- }: Props = $props();
21
- </script>
22
-
23
- <nav role="navigation" aria-label="Breadcrumb">
24
- <ol class="flex items-center gap-2">
25
- {#each items as item, index}
26
- <li class="flex items-center gap-2">
27
- {#if item.href}
28
- <a
29
- href={item.href}
30
- class="text-sm font-[var(--font-body)] text-[var(--color-text-soft)] hover:text-[var(--color-accent)] hover:underline transition-colors duration-200 ease-[var(--ease-luxe)]"
31
- >
32
- {item.label}
33
- </a>
34
- {:else if item.onclick}
35
- <button
36
- onclick={item.onclick}
37
- class="text-sm font-[var(--font-body)] text-[var(--color-text-soft)] hover:text-[var(--color-accent)] hover:underline transition-colors duration-200 ease-[var(--ease-luxe)] cursor-pointer"
38
- >
39
- {item.label}
40
- </button>
41
- {:else}
42
- <span class="text-sm font-[var(--font-body)] {index === items.length - 1 ? 'text-[var(--color-text)]' : 'text-[var(--color-text-soft)]'}">
43
- {item.label}
44
- </span>
45
- {/if}
46
-
47
- {#if index < items.length - 1}
48
- <span class="text-[var(--color-text-muted)] text-sm" aria-hidden="true">
49
- {#if separatorIcon}
50
- {@render separatorIcon()}
51
- {:else}
52
- {separator}
53
- {/if}
54
- </span>
55
- {/if}
56
- </li>
57
- {/each}
58
- </ol>
59
- </nav>
1
+ <script lang="ts">let { items, separator = '/', separatorIcon } = $props();
2
+ export {};
3
+ </script>
4
+
5
+ <nav role="navigation" aria-label="Breadcrumb">
6
+ <ol class="flex items-center gap-2">
7
+ {#each items as item, index}
8
+ <li class="flex items-center gap-2">
9
+ {#if item.href}
10
+ <a
11
+ href={item.href}
12
+ class="flex items-center gap-2 text-sm font-[var(--font-body)] text-[var(--color-text-soft)] hover:text-[var(--color-accent)] hover:underline transition-colors duration-200 ease-[var(--ease-luxe)]"
13
+ >
14
+ {#if item.icon}
15
+ <span class="inline-flex items-center justify-center">
16
+ {@render item.icon()}
17
+ </span>
18
+ {/if}
19
+ {item.label}
20
+ </a>
21
+ {:else if item.onclick}
22
+ <button
23
+ onclick={item.onclick}
24
+ class="flex items-center gap-2 text-sm font-[var(--font-body)] text-[var(--color-text-soft)] hover:text-[var(--color-accent)] hover:underline transition-colors duration-200 ease-[var(--ease-luxe)] cursor-pointer"
25
+ >
26
+ {#if item.icon}
27
+ <span class="inline-flex items-center justify-center">
28
+ {@render item.icon()}
29
+ </span>
30
+ {/if}
31
+ {item.label}
32
+ </button>
33
+ {:else}
34
+ <span class="flex items-center gap-2 text-sm font-[var(--font-body)] {index === items.length - 1 ? 'text-[var(--color-text)]' : 'text-[var(--color-text-soft)]'}">
35
+ {#if item.icon}
36
+ <span class="inline-flex items-center justify-center">
37
+ {@render item.icon()}
38
+ </span>
39
+ {/if}
40
+ {item.label}
41
+ </span>
42
+ {/if}
43
+
44
+ {#if index < items.length - 1}
45
+ <span class="text-[var(--color-text-muted)] text-sm" aria-hidden="true">
46
+ {#if separatorIcon}
47
+ {@render separatorIcon()}
48
+ {:else}
49
+ {separator}
50
+ {/if}
51
+ </span>
52
+ {/if}
53
+ </li>
54
+ {/each}
55
+ </ol>
56
+ </nav>
@@ -3,6 +3,7 @@ interface BreadcrumbItem {
3
3
  label: string;
4
4
  href?: string;
5
5
  onclick?: () => void;
6
+ icon?: Snippet;
6
7
  }
7
8
  interface Props {
8
9
  items: BreadcrumbItem[];