@hashrytech/quick-components-kit 0.18.1 → 0.19.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 (51) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/components/button/Button.svelte +6 -5
  3. package/dist/components/button/Button.svelte.d.ts +3 -2
  4. package/dist/components/button/index.d.ts +1 -1
  5. package/dist/components/hamburger-menu/HamburgerMenu.svelte +2 -2
  6. package/dist/components/hamburger-menu/HamburgerMenu.svelte.d.ts +1 -1
  7. package/dist/components/select/Select.svelte +52 -20
  8. package/dist/components/select/Select.svelte.d.ts +7 -3
  9. package/dist/components/text-input/TextInput.svelte +88 -18
  10. package/dist/components/text-input/TextInput.svelte.d.ts +13 -3
  11. package/dist/index.d.ts +4 -0
  12. package/dist/index.js +4 -0
  13. package/dist/ui/banners/banner-1/Banner1.svelte +47 -0
  14. package/dist/ui/banners/banner-1/Banner1.svelte.d.ts +14 -0
  15. package/dist/ui/banners/banner-1/index.d.ts +1 -0
  16. package/dist/ui/banners/banner-1/index.js +1 -0
  17. package/dist/ui/breadcrumbs/breadcrumbs-1/Breadcrumbs1.svelte +30 -0
  18. package/dist/ui/breadcrumbs/breadcrumbs-1/Breadcrumbs1.svelte.d.ts +13 -0
  19. package/dist/ui/breadcrumbs/breadcrumbs-1/index.d.ts +1 -0
  20. package/dist/ui/breadcrumbs/breadcrumbs-1/index.js +1 -0
  21. package/dist/ui/featured-products/featured-products-1/FeaturedProducts1.svelte +56 -0
  22. package/dist/ui/featured-products/featured-products-1/FeaturedProducts1.svelte.d.ts +22 -0
  23. package/dist/ui/featured-products/featured-products-1/index.d.ts +1 -0
  24. package/dist/ui/featured-products/featured-products-1/index.js +1 -0
  25. package/dist/ui/footers/footer-1/Footer1.svelte +81 -0
  26. package/dist/ui/footers/footer-1/Footer1.svelte.d.ts +18 -0
  27. package/dist/ui/footers/footer-1/index.d.ts +1 -0
  28. package/dist/ui/footers/footer-1/index.js +1 -0
  29. package/dist/ui/headers/header-1/Header1.svelte +94 -0
  30. package/dist/ui/headers/header-1/Header1.svelte.d.ts +15 -0
  31. package/dist/ui/headers/header-1/index.d.ts +1 -0
  32. package/dist/ui/headers/header-1/index.js +1 -0
  33. package/dist/ui/product-filters/product-filter-1/ProductFilter1.svelte +42 -0
  34. package/dist/ui/product-filters/product-filter-1/ProductFilter1.svelte.d.ts +26 -0
  35. package/dist/ui/product-filters/product-filter-1/ProductFilterBlock.svelte +49 -0
  36. package/dist/ui/product-filters/product-filter-1/ProductFilterBlock.svelte.d.ts +17 -0
  37. package/dist/ui/product-filters/product-filter-1/index.d.ts +1 -0
  38. package/dist/ui/product-filters/product-filter-1/index.js +1 -0
  39. package/dist/ui/product-list/product-list-1/ProductList1.svelte +76 -0
  40. package/dist/ui/product-list/product-list-1/ProductList1.svelte.d.ts +24 -0
  41. package/dist/ui/product-list/product-list-1/index.d.ts +1 -0
  42. package/dist/ui/product-list/product-list-1/index.js +1 -0
  43. package/dist/ui/product-list-navigation/product-list-navigation-1/ProductListNavigation1.svelte +51 -0
  44. package/dist/ui/product-list-navigation/product-list-navigation-1/ProductListNavigation1.svelte.d.ts +12 -0
  45. package/dist/ui/product-list-navigation/product-list-navigation-1/index.d.ts +1 -0
  46. package/dist/ui/product-list-navigation/product-list-navigation-1/index.js +1 -0
  47. package/dist/ui/searchbox/Searchbox.svelte +46 -0
  48. package/dist/ui/searchbox/Searchbox.svelte.d.ts +13 -0
  49. package/dist/ui/searchbox/index.d.ts +1 -0
  50. package/dist/ui/searchbox/index.js +1 -0
  51. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # @hashrytech/quick-components-kit
2
2
 
3
+ ## 0.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Update: adding ui components and refactoring textbox and select components
3
8
  ## 0.18.1
4
9
 
5
10
  ### Patch Changes
@@ -6,7 +6,8 @@
6
6
  disabled?: boolean;
7
7
  children?: Snippet;
8
8
  icon?: Snippet;
9
- activeIcon?: Snippet;
9
+ loadingIcon?: Snippet;
10
+ loading?: boolean;
10
11
  onclick?: (event: MouseEvent) => void;
11
12
  class?: ClassNameValue;
12
13
  };
@@ -16,15 +17,15 @@
16
17
  <script lang="ts">
17
18
  import {twMerge} from 'tailwind-merge';
18
19
 
19
- let { disabled=$bindable(), children, icon, activeIcon, onclick, ...props }: ButtonProps = $props();
20
+ let { disabled=$bindable(), loading=$bindable(false), children, icon, loadingIcon, onclick, ...props }: ButtonProps = $props();
20
21
 
21
22
  </script>
22
23
 
23
24
  <button {disabled} class={twMerge("flex flex-row items-center gap-2 px-4 py-2 focus:outline-primary-focus bg-primary-button hover:bg-primary-button-hover rounded-primary cursor-pointer focus:ring-primary-focus focus:ring",
24
25
  "disabled:bg-primary-button/60 disabled:cursor-default", props.class)}
25
- {onclick}>
26
- {#if activeIcon}
27
- <span>{@render activeIcon()}</span>
26
+ {onclick}>
27
+ {#if loadingIcon && loading}
28
+ <span class="shrink-0 animate-spin font-semibold">{@render loadingIcon()}</span>
28
29
  {:else if icon}
29
30
  <span>{@render icon()}</span>
30
31
  {/if}
@@ -4,10 +4,11 @@ export type ButtonProps = {
4
4
  disabled?: boolean;
5
5
  children?: Snippet;
6
6
  icon?: Snippet;
7
- activeIcon?: Snippet;
7
+ loadingIcon?: Snippet;
8
+ loading?: boolean;
8
9
  onclick?: (event: MouseEvent) => void;
9
10
  class?: ClassNameValue;
10
11
  };
11
- declare const Button: import("svelte").Component<ButtonProps, {}, "disabled">;
12
+ declare const Button: import("svelte").Component<ButtonProps, {}, "disabled" | "loading">;
12
13
  type Button = ReturnType<typeof Button>;
13
14
  export default Button;
@@ -1 +1 @@
1
- export { default as Button } from './Button.svelte';
1
+ export { default as Button, type ButtonProps } from './Button.svelte';
@@ -4,7 +4,7 @@
4
4
  export type HamburgerProps = {
5
5
  open?: boolean;
6
6
  useCloseBtn?: boolean;
7
- ariaLabel: string;
7
+ ariaLabel?: string;
8
8
  linesClasses?: string;
9
9
  linesParentClasses?: string;
10
10
  onclick?: (event: MouseEvent) => void;
@@ -16,7 +16,7 @@
16
16
  <script lang="ts">
17
17
  import {twMerge} from 'tailwind-merge';
18
18
 
19
- let { open=$bindable(true), ariaLabel, linesClasses, linesParentClasses, useCloseBtn=true, onclick, ...props }: HamburgerProps = $props();
19
+ let { open=$bindable(true), ariaLabel="menu button", linesClasses, linesParentClasses, useCloseBtn=true, onclick, ...props }: HamburgerProps = $props();
20
20
 
21
21
  </script>
22
22
 
@@ -2,7 +2,7 @@ import type { ClassNameValue } from 'tailwind-merge';
2
2
  export type HamburgerProps = {
3
3
  open?: boolean;
4
4
  useCloseBtn?: boolean;
5
- ariaLabel: string;
5
+ ariaLabel?: string;
6
6
  linesClasses?: string;
7
7
  linesParentClasses?: string;
8
8
  onclick?: (event: MouseEvent) => void;
@@ -11,16 +11,20 @@
11
11
  export type SelectProps = {
12
12
  id: string;
13
13
  name?: string;
14
- label?: string;
14
+ labelText?: string;
15
15
  disabled?: boolean;
16
16
  value?: string | number;
17
- options: Option[];
17
+ options?: Option[];
18
18
  size?: "sm" | "md" | "lg";
19
19
  labelPosition?: "left" | "top" | "right" | "bottom";
20
20
  class?: ClassNameValue;
21
21
  labelClass?: ClassNameValue;
22
+ firstDivClass?: ClassNameValue;
23
+ secondDivClass?: ClassNameValue;
22
24
  optionsClass?: ClassNameValue;
23
- parentDivClass?: ClassNameValue;
25
+ error?: string;
26
+ label?: Snippet;
27
+ optionsSnippet?: Snippet;
24
28
  icon?: Snippet;
25
29
  onchange?: (event: Event) => void;
26
30
  };
@@ -29,12 +33,30 @@
29
33
  <script lang="ts">
30
34
  import { twMerge } from 'tailwind-merge';
31
35
 
32
- let { id, name, label = "", labelPosition = "top", value = $bindable(), options=$bindable([]), size = "md", disabled = $bindable(false), labelClass, parentDivClass, optionsClass, onchange, ...restProps}: SelectProps = $props();
36
+ let {
37
+ id,
38
+ name,
39
+ label,
40
+ labelText = "",
41
+ labelPosition = "right",
42
+ value = $bindable(),
43
+ options=$bindable([]),
44
+ size = "md",
45
+ disabled = $bindable(false),
46
+ firstDivClass,
47
+ secondDivClass,
48
+ labelClass,
49
+ optionsClass,
50
+ error,
51
+ onchange,
52
+ icon,
53
+ optionsSnippet,
54
+ ...restProps}: SelectProps = $props();
33
55
 
34
56
  const sizeMap = {
35
- sm: "text-sm py-1 px-2 h-8",
36
- md: "text-base py-2 px-3 h-10",
37
- lg: "text-lg py-3 px-4 h-12",
57
+ sm: "text-sm py-1 h-[2.05rem]",
58
+ md: "text-base py-2 h-[2.375rem]",
59
+ lg: "text-lg py-3 h-[2.8rem]",
38
60
  };
39
61
 
40
62
  const directionClass = {
@@ -45,18 +67,28 @@
45
67
  };
46
68
  </script>
47
69
 
48
- <div class={twMerge("flex", directionClass[labelPosition] ?? directionClass.top, parentDivClass)}>
49
- {#if label}
50
- <label for={id} class={twMerge("text-sm font-medium text-primary-label-text ml-1", labelClass)}>{label}</label>
51
- {/if}
70
+ <div class={twMerge("flex flex-col gap-1", firstDivClass)}>
71
+ <div class={twMerge("flex", directionClass[labelPosition] ?? directionClass.top, secondDivClass)}>
72
+ {#if label}
73
+ {@render label()}
74
+ {:else}
75
+ {#if labelText}
76
+ <label for={id} class={twMerge("text-sm font-medium text-primary-label-text ml-1 w-full", labelClass)}>{labelText}</label>
77
+ {/if}
78
+ {/if}
52
79
 
53
- <select {id} name={name ?? id} bind:value {disabled} onchange={onchange}
54
- class={twMerge("rounded-primary border border-primary-input-border focus:border-primary-focus focus:ring-primary-focus placeholder:opacity-50 disabled:bg-neutral-300/30 disabled:border-neutral-300/30",
55
- sizeMap[size], restProps.class)}>
56
- {#each options as option}
57
- <option value={option.value} disabled={option.disabled} class={twMerge("", optionsClass)}>
58
- {option.label}
59
- </option>
60
- {/each}
61
- </select>
80
+ <select {id} name={name ?? id} bind:value {disabled} onchange={onchange}
81
+ class={twMerge("rounded-primary border border-primary-input-border focus:border-primary-focus focus:ring-primary-focus placeholder:opacity-50 disabled:bg-neutral-300/30 disabled:border-neutral-300/30",
82
+ sizeMap[size], error ? "bg-red-50 border-red-300" : "", restProps.class)}>
83
+ {@render optionsSnippet?.()}
84
+ {#each options as option}
85
+ <option value={option.value} disabled={option.disabled} class={twMerge("", optionsClass)}>
86
+ {option.label}
87
+ </option>
88
+ {/each}
89
+ </select>
90
+ </div>
91
+ {#if error}
92
+ <p class="text-sm text-red-500 mt-0.5 bg-red-100/30 px-2 rounded-primary">{error}</p>
93
+ {/if}
62
94
  </div>
@@ -8,16 +8,20 @@ export type Option = {
8
8
  export type SelectProps = {
9
9
  id: string;
10
10
  name?: string;
11
- label?: string;
11
+ labelText?: string;
12
12
  disabled?: boolean;
13
13
  value?: string | number;
14
- options: Option[];
14
+ options?: Option[];
15
15
  size?: "sm" | "md" | "lg";
16
16
  labelPosition?: "left" | "top" | "right" | "bottom";
17
17
  class?: ClassNameValue;
18
18
  labelClass?: ClassNameValue;
19
+ firstDivClass?: ClassNameValue;
20
+ secondDivClass?: ClassNameValue;
19
21
  optionsClass?: ClassNameValue;
20
- parentDivClass?: ClassNameValue;
22
+ error?: string;
23
+ label?: Snippet;
24
+ optionsSnippet?: Snippet;
21
25
  icon?: Snippet;
22
26
  onchange?: (event: Event) => void;
23
27
  };
@@ -1,5 +1,6 @@
1
1
  <script lang="ts" module>
2
2
  import type { Snippet } from 'svelte';
3
+ import type { FullAutoFill } from 'svelte/elements';
3
4
  import type { ClassNameValue } from 'tailwind-merge';
4
5
 
5
6
  /**
@@ -9,7 +10,7 @@
9
10
  * - "lg": h-[2.8rem] text-lg placeholder:text-lg
10
11
  */
11
12
  export type TextInputSize = "sm" | "md" | "lg";
12
- export type TextInputType = "text" | "password" | "number" | "email" | "tel" | "url";
13
+ export type TextInputType = "text" | "password" | "number" | "email" | "tel" | "url" | "search";
13
14
 
14
15
  /**
15
16
  * Props for the TextBox component.
@@ -35,14 +36,23 @@
35
36
  type?: TextInputType;
36
37
  placeholder?: string;
37
38
  labelText?: string;
39
+ labelPosition?: "top" | "left" | "right" | "bottom";
38
40
  size?: TextInputSize;
39
41
  disabled?: boolean;
40
42
  required?: boolean;
41
43
  error?: string;
42
- onchange?: () => void;
44
+ labelClass?: ClassNameValue;
45
+ firstDivClass?: ClassNameValue;
46
+ secondDivClass?: ClassNameValue;
47
+ thirdDivClass?: ClassNameValue;
48
+ autocomplete?: FullAutoFill | null;
49
+ debounceDelay?: number;
50
+ onInput?: (value: string) => void;
51
+ onchange?: (event: Event) => void;
43
52
  onmouseup?: () => void;
44
53
  label?: Snippet;
45
- icon?: Snippet;
54
+ leftIcon?: Snippet;
55
+ rightIcon?: Snippet;
46
56
  class?: ClassNameValue;
47
57
  };
48
58
 
@@ -51,8 +61,32 @@
51
61
 
52
62
  <script lang="ts">
53
63
  import { twMerge } from 'tailwind-merge';
54
-
55
- let { id, type="text", name="", value=$bindable(""), placeholder="", labelText, size="md", disabled=false, required=false, error, onchange, onmouseup, label, icon, ...props}: TextInputProps = $props();
64
+
65
+ let {
66
+ id,
67
+ type="text",
68
+ name="",
69
+ value=$bindable(""),
70
+ placeholder="",
71
+ labelText="",
72
+ labelClass,
73
+ labelPosition="top",
74
+ size="md",
75
+ disabled=false,
76
+ required=false,
77
+ error,
78
+ firstDivClass,
79
+ secondDivClass,
80
+ thirdDivClass,
81
+ autocomplete,
82
+ debounceDelay=300, //ms
83
+ onchange,
84
+ onInput,
85
+ onmouseup,
86
+ label,
87
+ leftIcon,
88
+ rightIcon,
89
+ ...restProps}: TextInputProps = $props();
56
90
 
57
91
  /**
58
92
  * Predefined size classes for the TextBox input.
@@ -61,25 +95,61 @@
61
95
  * - "lg": h-[2.8rem] text-lg placeholder:text-lg
62
96
  */
63
97
  let sizeStyle: Record<TextInputSize, string> = {
64
- sm: "h-[2.05rem] text-sm placeholder:text-sm",
65
- md: "h-[2.375rem] text-sm placeholder:text-sm",
66
- lg: "h-[2.8rem] text-base placeholder:text-base"
98
+ sm: "text-sm placeholder:text-sm",
99
+ md: "text-sm placeholder:text-sm",
100
+ lg: "text-base placeholder:text-base"
101
+ };
102
+
103
+ let textBoxStyle: Record<TextInputSize, string> = {
104
+ sm: "h-[2.05rem] pl-2 pr-1.5",
105
+ md: "h-[2.375rem]",
106
+ lg: "h-[2.8rem] pl-3 pr-2"
107
+ };
108
+
109
+ const directionClass = {
110
+ top: "flex-col gap-1",
111
+ bottom: "flex-col-reverse gap-1",
112
+ left: "flex-row items-center gap-2",
113
+ right: "flex-row-reverse items-center gap-2",
67
114
  };
68
115
 
116
+ // --- Debounce logic ---
117
+ let localValue = value; // local for immediate typing
118
+ let debounceTimer: ReturnType<typeof setTimeout>;
119
+
120
+ function handleInput(e: Event) {
121
+ localValue = (e.target as HTMLInputElement).value;
122
+ clearTimeout(debounceTimer);
123
+ debounceTimer = setTimeout(() => {
124
+ value = localValue; // sync to bound value after delay
125
+ onInput?.(value); // call handler if provided
126
+ }, debounceDelay);
127
+ }
128
+
69
129
  </script>
70
130
 
71
- <div class="flex flex-col gap-1 w-full">
72
- {#if label}{@render label()}{:else}{#if labelText}<label for={id} class="text-sm font-medium text-primary-label-text ml-1">{labelText}</label>{/if}{/if}
73
- <div class="relative">
74
- {#if icon}<div class="absolute inset-y-0 left-0 flex items-center justify-center rounded-l-primary m-0.5 w-10">{@render icon()}</div>{/if}
75
- <input {disabled} {required} {type} {id} name={name ? name: id} {placeholder} {onchange} {onmouseup} bind:value
76
- class={twMerge("rounded-primary border border-primary-input-border focus:border-primary-focus focus:ring-primary-focus placeholder:opacity-50 disabled:bg-neutral-300/30 disabled:border-neutral-300/30",
77
- error ? "bg-red-50 border-red-300" : "",
78
- icon ? "pl-10" : "",
79
- sizeStyle[size], props.class)}
80
- />
131
+ <div class={twMerge("", firstDivClass)}>
132
+ <div class={twMerge("flex rounded-primary", directionClass[labelPosition] ?? directionClass.top, secondDivClass)}>
133
+
134
+ {#if label}{@render label()}{/if}
135
+ {#if !label && labelText}<label for={id} class={twMerge("text-sm font-medium text-primary-label-text ml-1", labelClass)}>{labelText}</label>{/if}
136
+
137
+ <!-- Text Box -->
138
+ <div class={twMerge("flex flex-row items-center rounded-primary border border-primary-input-border gap-2 focus-within:ring focus-within:border-primary-focus focus-within:ring-primary-focus has-[input:disabled]:bg-neutral-300/30 has-[input:disabled]:border-neutral-300/30",
139
+ error ? "bg-red-50 border-red-300" : "", textBoxStyle[size], thirdDivClass)}>
140
+
141
+ {#if leftIcon}<div class="h-full flex flex-col items-center justify-center">{@render leftIcon()}</div>{/if}
142
+
143
+ <input {disabled} {required} {type} {id} name={name ? name: id} {placeholder} {onmouseup} bind:value {autocomplete} oninput={handleInput}
144
+ class={twMerge("border-0 focus:border-0 focus:ring-0 active:border-0 outline-none p-0 bg-transparent placeholder:text-neutral-600/50 h-full w-full", sizeStyle[size], restProps.class)} />
145
+
146
+ {#if rightIcon}<div class="h-full flex flex-col items-center justify-center">{@render rightIcon()}</div>{/if}
147
+ </div>
148
+
81
149
  </div>
150
+
82
151
  {#if error}
83
152
  <p class="text-sm text-red-500 mt-0.5 bg-red-100/30 px-2 rounded-primary">{error}</p>
84
153
  {/if}
154
+
85
155
  </div>
@@ -1,4 +1,5 @@
1
1
  import type { Snippet } from 'svelte';
2
+ import type { FullAutoFill } from 'svelte/elements';
2
3
  import type { ClassNameValue } from 'tailwind-merge';
3
4
  /**
4
5
  * Predefined size classes for the TextBox input.
@@ -7,7 +8,7 @@ import type { ClassNameValue } from 'tailwind-merge';
7
8
  * - "lg": h-[2.8rem] text-lg placeholder:text-lg
8
9
  */
9
10
  export type TextInputSize = "sm" | "md" | "lg";
10
- export type TextInputType = "text" | "password" | "number" | "email" | "tel" | "url";
11
+ export type TextInputType = "text" | "password" | "number" | "email" | "tel" | "url" | "search";
11
12
  /**
12
13
  * Props for the TextBox component.
13
14
  *
@@ -32,14 +33,23 @@ export type TextInputProps = {
32
33
  type?: TextInputType;
33
34
  placeholder?: string;
34
35
  labelText?: string;
36
+ labelPosition?: "top" | "left" | "right" | "bottom";
35
37
  size?: TextInputSize;
36
38
  disabled?: boolean;
37
39
  required?: boolean;
38
40
  error?: string;
39
- onchange?: () => void;
41
+ labelClass?: ClassNameValue;
42
+ firstDivClass?: ClassNameValue;
43
+ secondDivClass?: ClassNameValue;
44
+ thirdDivClass?: ClassNameValue;
45
+ autocomplete?: FullAutoFill | null;
46
+ debounceDelay?: number;
47
+ onInput?: (value: string) => void;
48
+ onchange?: (event: Event) => void;
40
49
  onmouseup?: () => void;
41
50
  label?: Snippet;
42
- icon?: Snippet;
51
+ leftIcon?: Snippet;
52
+ rightIcon?: Snippet;
43
53
  class?: ClassNameValue;
44
54
  };
45
55
  declare const TextInput: import("svelte").Component<TextInputProps, {}, "value">;
package/dist/index.d.ts CHANGED
@@ -22,3 +22,7 @@ export * from './modules/api-proxy.js';
22
22
  export * from './modules/crypto.js';
23
23
  export * from './modules/problem-details.js';
24
24
  export * from './functions/object-to-form-data.js';
25
+ export * from './ui/headers/header-1/index.js';
26
+ export * from './ui/footers/footer-1/index.js';
27
+ export * from './ui/banners/banner-1/index.js';
28
+ export * from './ui/featured-products/featured-products-1/index.js';
package/dist/index.js CHANGED
@@ -24,4 +24,8 @@ export * from './modules/api-proxy.js';
24
24
  export * from './modules/crypto.js';
25
25
  export * from './modules/problem-details.js';
26
26
  export * from './functions/object-to-form-data.js';
27
+ export * from './ui/headers/header-1/index.js';
28
+ export * from './ui/footers/footer-1/index.js';
29
+ export * from './ui/banners/banner-1/index.js';
30
+ export * from './ui/featured-products/featured-products-1/index.js';
27
31
  // Add more components here...
@@ -0,0 +1,47 @@
1
+ <script lang="ts" module>
2
+ import type { ClassNameValue } from 'tailwind-merge';
3
+
4
+ export type Banner1Props = {
5
+ mainText: string;
6
+ subText?: string;
7
+ image?: string;
8
+ primaryButtonText?: string;
9
+ primaryButtonLink?: string;
10
+ secondaryButtonText?: string;
11
+ secondaryButtonLink?: string;
12
+ class?: ClassNameValue;
13
+ };
14
+
15
+ </script>
16
+
17
+ <script lang="ts">
18
+ import { twMerge } from "tailwind-merge";
19
+
20
+ let { mainText, subText, image, primaryButtonText, primaryButtonLink, secondaryButtonText, secondaryButtonLink, ...restProps }: Banner1Props = $props();
21
+ let style = image ? "background-image: url('" + image + "');" : "";
22
+
23
+ </script>
24
+
25
+ <section class={twMerge("lg:grid", restProps.class)} {style}>
26
+ <div class="mx-auto w-screen max-w-screen-xl px-4 py-16 sm:px-6 sm:py-24 lg:px-8 lg:py-32">
27
+ <div class="mx-auto max-w-prose text-center">
28
+ <h1 class="text-4xl font-bold text-gray-900 sm:text-5xl">{@html mainText}</h1>
29
+ <p class="mt-4 text-base text-pretty text-gray-700 sm:text-lg/relaxed dark:text-gray-200">{subText}</p>
30
+
31
+ {#if primaryButtonText || secondaryButtonText}
32
+ <div class="mt-4 flex justify-center gap-4 sm:mt-6">
33
+ {#if primaryButtonText && primaryButtonLink}
34
+ <a class="inline-block rounded-primary bg-primary-button px-5 py-3 font-medium text-white shadow-primary transition-colors hover:bg-primary-button-hover"
35
+ href={primaryButtonLink}>{primaryButtonText}</a>
36
+ {/if}
37
+
38
+ {#if secondaryButtonText && secondaryButtonLink}
39
+ <a class="inline-block bg-white rounded-primary border border-gray-200 px-5 py-3 font-medium text-gray-700 shadow-primary transition-colors
40
+ hover:bg-gray-50 hover:text-gray-900" href={secondaryButtonLink}>
41
+ {secondaryButtonText}</a>
42
+ {/if}
43
+ </div>
44
+ {/if}
45
+ </div>
46
+ </div>
47
+ </section>
@@ -0,0 +1,14 @@
1
+ import type { ClassNameValue } from 'tailwind-merge';
2
+ export type Banner1Props = {
3
+ mainText: string;
4
+ subText?: string;
5
+ image?: string;
6
+ primaryButtonText?: string;
7
+ primaryButtonLink?: string;
8
+ secondaryButtonText?: string;
9
+ secondaryButtonLink?: string;
10
+ class?: ClassNameValue;
11
+ };
12
+ declare const Banner1: import("svelte").Component<Banner1Props, {}, "">;
13
+ type Banner1 = ReturnType<typeof Banner1>;
14
+ export default Banner1;
@@ -0,0 +1 @@
1
+ export { default as Banner1 } from './Banner1.svelte';
@@ -0,0 +1 @@
1
+ export { default as Banner1 } from './Banner1.svelte';
@@ -0,0 +1,30 @@
1
+ <script lang="ts">
2
+ import { twMerge, type ClassNameValue } from "tailwind-merge";
3
+ let {filters=$bindable([]), onRemove, onClear, ...restProps}:
4
+ {
5
+ filters: { key: string; value: string }[];
6
+ onRemove: (key: string, value: string) => void;
7
+ onClear: () => void;
8
+ class?: ClassNameValue;
9
+ } = $props();
10
+
11
+ </script>
12
+
13
+ <nav aria-label="Breadcrumb filters" class={twMerge("flex flex-wrap items-center gap-2 text-sm", restProps.class)}>
14
+
15
+ <!-- Dynamic filters -->
16
+ {#if filters.length > 0}
17
+ {#each filters as { key, value }}
18
+ <button class="flex items-center gap-1 rounded-full bg-neutral-100 px-3 py-1 text-neutral-700 hover:bg-neutral-200 cursor-pointer break-all" onclick={() => onRemove?.(key, value)}>
19
+ <span class="capitalize text-neutral-600">{key}:</span>
20
+ <span class="font-medium">{value}</span>
21
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="h-4 w-4 text-neutral-500 shrink-0">
22
+ <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0
23
+ 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
24
+ </svg>
25
+ </button>
26
+ {/each}
27
+ <!-- Clear all -->
28
+ <button class="text-red-600 hover:underline cursor-pointer" onclick={onClear}>Clear all</button>
29
+ {/if}
30
+ </nav>
@@ -0,0 +1,13 @@
1
+ import { type ClassNameValue } from "tailwind-merge";
2
+ type $$ComponentProps = {
3
+ filters: {
4
+ key: string;
5
+ value: string;
6
+ }[];
7
+ onRemove: (key: string, value: string) => void;
8
+ onClear: () => void;
9
+ class?: ClassNameValue;
10
+ };
11
+ declare const Breadcrumbs1: import("svelte").Component<$$ComponentProps, {}, "filters">;
12
+ type Breadcrumbs1 = ReturnType<typeof Breadcrumbs1>;
13
+ export default Breadcrumbs1;
@@ -0,0 +1 @@
1
+ export { default as Breadcrumbs1 } from './Breadcrumbs1.svelte';
@@ -0,0 +1 @@
1
+ export { default as Breadcrumbs1 } from './Breadcrumbs1.svelte';
@@ -0,0 +1,56 @@
1
+ <script lang="ts" module>
2
+ import type { ClassNameValue } from 'tailwind-merge';
3
+
4
+ export type FeaturedProducts1Props = {
5
+ title: string;
6
+ titleDescription?: string;
7
+ products: {uid: string, title: string, price: string, price_compare: string, image: string, badges?: string[], swatches?: string[]}[];
8
+ titleClass?: ClassNameValue;
9
+ imageClass?: ClassNameValue;
10
+ priceClass?: ClassNameValue;
11
+ priceCompareClass?: ClassNameValue;
12
+ class?: ClassNameValue;
13
+ };
14
+
15
+ </script>
16
+
17
+ <script lang="ts">
18
+ import { twMerge } from "tailwind-merge";
19
+
20
+ let { title, products, titleClass, titleDescription, imageClass, priceClass, priceCompareClass, ...restProps }: FeaturedProducts1Props = $props();
21
+
22
+ </script>
23
+
24
+ <section class="space-y-6">
25
+ <div class="text-center">
26
+ <h2 class="text-2xl font-semibold">{title}</h2>
27
+ <div class="w-32 bg-primary-button h-0.5 rounded-primary mx-auto mt-2"></div>
28
+ {#if titleDescription}
29
+ <p class="mt-6 px-6 max-w-2xl mx-auto">{titleDescription}</p>
30
+ {/if}
31
+ </div>
32
+
33
+ <div class="mx-auto p-2">
34
+ <div class={twMerge("grid grid-cols-1 xs:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 mx-auto w-fit p-1", restProps.class)}>
35
+ {#each products as product}
36
+ <a href={`/products/${product.uid}`} class="border border-primary-card-border shadow-primary rounded-primary overflow-hidden bg-white">
37
+
38
+ <div class="aspect-[4/3] rounded-primary">
39
+ <img src={product.image} alt={product.title} class={twMerge("object-cover w-full h-[250px]", imageClass)} />
40
+ </div>
41
+
42
+ <div class="p-3">
43
+ <h3 class={twMerge("text-base font-medium text-neutral-900", titleClass)}>{product.title}</h3>
44
+ <div class="mt-1 flex items-center gap-2">
45
+ <span class={twMerge("font-medium", priceClass)}>${product.price}</span>
46
+ {#if product.price_compare}
47
+ <span class={twMerge("text-base text-neutral-500 line-through", priceCompareClass)}>${product.price_compare}</span>
48
+ {/if}
49
+ </div>
50
+ </div>
51
+ </a>
52
+ {/each}
53
+ </div>
54
+ </div>
55
+
56
+ </section>
@@ -0,0 +1,22 @@
1
+ import type { ClassNameValue } from 'tailwind-merge';
2
+ export type FeaturedProducts1Props = {
3
+ title: string;
4
+ titleDescription?: string;
5
+ products: {
6
+ uid: string;
7
+ title: string;
8
+ price: string;
9
+ price_compare: string;
10
+ image: string;
11
+ badges?: string[];
12
+ swatches?: string[];
13
+ }[];
14
+ titleClass?: ClassNameValue;
15
+ imageClass?: ClassNameValue;
16
+ priceClass?: ClassNameValue;
17
+ priceCompareClass?: ClassNameValue;
18
+ class?: ClassNameValue;
19
+ };
20
+ declare const FeaturedProducts1: import("svelte").Component<FeaturedProducts1Props, {}, "">;
21
+ type FeaturedProducts1 = ReturnType<typeof FeaturedProducts1>;
22
+ export default FeaturedProducts1;
@@ -0,0 +1 @@
1
+ export { default as FeaturedProducts1 } from './FeaturedProducts1.svelte';
@@ -0,0 +1 @@
1
+ export { default as FeaturedProducts1 } from './FeaturedProducts1.svelte';