@valerius_petrini/corekit-ui 0.1.66 → 0.1.69

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 (182) hide show
  1. package/dist/actions/toast.svelte.d.ts +1 -1
  2. package/dist/actions/toast.svelte.js +1 -1
  3. package/dist/components/display/Card/index.stories.svelte +35 -0
  4. package/dist/components/display/Card/index.stories.svelte.d.ts +18 -0
  5. package/dist/components/{Card.svelte → display/Card/index.svelte} +4 -3
  6. package/dist/components/display/Card/index.svelte.d.ts +4 -0
  7. package/dist/{types/Card.d.ts → components/display/Card/types.d.ts} +2 -3
  8. package/dist/components/display/Card/variant.d.ts +2 -0
  9. package/dist/components/display/KBD/index.stories.svelte +13 -0
  10. package/dist/components/display/KBD/index.stories.svelte.d.ts +18 -0
  11. package/dist/components/display/KBD/index.svelte.d.ts +3 -0
  12. package/dist/components/{Skeleton.svelte → display/Skeleton/index.svelte} +2 -2
  13. package/dist/components/display/Skeleton/index.svelte.d.ts +4 -0
  14. package/dist/{types/Skeleton.d.ts → components/display/Skeleton/types.d.ts} +1 -1
  15. package/dist/components/{Table.svelte → display/Table/index.svelte} +1 -1
  16. package/dist/components/display/Table/index.svelte.d.ts +4 -0
  17. package/dist/{types/Table.d.ts → components/display/Table/types.d.ts} +2 -2
  18. package/dist/components/display/index.d.ts +7 -0
  19. package/dist/components/display/index.js +4 -0
  20. package/dist/components/{Loader.svelte → feedback/Loader/index.svelte} +3 -3
  21. package/dist/components/feedback/Loader/index.svelte.d.ts +4 -0
  22. package/dist/components/feedback/Loader/types.d.ts +7 -0
  23. package/dist/components/{Modal.svelte → feedback/Modal/index.svelte} +3 -3
  24. package/dist/components/feedback/Modal/index.svelte.d.ts +4 -0
  25. package/dist/components/feedback/Modal/types.d.ts +6 -0
  26. package/dist/components/{Progress.svelte → feedback/Progress/index.svelte} +3 -3
  27. package/dist/components/feedback/Progress/index.svelte.d.ts +4 -0
  28. package/dist/{types/Progress.d.ts → components/feedback/Progress/types.d.ts} +3 -3
  29. package/dist/components/{Toast.svelte → feedback/Toast/index.svelte} +6 -6
  30. package/dist/components/feedback/Toast/index.svelte.d.ts +5 -0
  31. package/dist/{types/Toast.d.ts → components/feedback/Toast/types.d.ts} +3 -3
  32. package/dist/components/{Toaster.svelte → feedback/Toaster/index.svelte} +3 -3
  33. package/dist/components/feedback/Toaster/index.svelte.d.ts +3 -0
  34. package/dist/components/feedback/index.d.ts +9 -0
  35. package/dist/components/feedback/index.js +5 -0
  36. package/dist/components/inputs/Button/index.stories.svelte +53 -0
  37. package/dist/components/inputs/Button/index.stories.svelte.d.ts +18 -0
  38. package/dist/components/inputs/Button/index.svelte +98 -0
  39. package/dist/components/inputs/Button/index.svelte.d.ts +4 -0
  40. package/dist/components/inputs/Button/size.d.ts +3 -0
  41. package/dist/components/inputs/Button/size.js +28 -0
  42. package/dist/{types/Button.d.ts → components/inputs/Button/types.d.ts} +4 -4
  43. package/dist/components/{Checkbox.svelte → inputs/Checkbox/index.svelte} +2 -2
  44. package/dist/components/inputs/Checkbox/index.svelte.d.ts +4 -0
  45. package/dist/{types/Checkbox.d.ts → components/inputs/Checkbox/types.d.ts} +1 -1
  46. package/dist/components/inputs/ColorInput/index.stories.svelte +23 -0
  47. package/dist/components/inputs/ColorInput/index.stories.svelte.d.ts +18 -0
  48. package/dist/components/inputs/ColorInput/index.svelte +384 -0
  49. package/dist/components/inputs/ColorInput/index.svelte.d.ts +4 -0
  50. package/dist/components/inputs/ColorInput/types.d.ts +16 -0
  51. package/dist/components/inputs/Combobox/index.stories.svelte +34 -0
  52. package/dist/components/inputs/Combobox/index.stories.svelte.d.ts +18 -0
  53. package/dist/components/{Combobox.svelte → inputs/Combobox/index.svelte} +67 -10
  54. package/dist/components/inputs/Combobox/index.svelte.d.ts +4 -0
  55. package/dist/components/inputs/Combobox/types.d.ts +8 -0
  56. package/dist/components/{FileInput.svelte → inputs/FileInput/index.svelte} +4 -4
  57. package/dist/components/inputs/FileInput/index.svelte.d.ts +4 -0
  58. package/dist/components/inputs/FileInput/types.d.ts +14 -0
  59. package/dist/components/inputs/Input/index.stories.svelte +27 -0
  60. package/dist/components/inputs/Input/index.stories.svelte.d.ts +18 -0
  61. package/dist/components/{Input.svelte → inputs/Input/index.svelte} +8 -11
  62. package/dist/components/inputs/Input/index.svelte.d.ts +5 -0
  63. package/dist/components/inputs/Input/types.d.ts +15 -0
  64. package/dist/components/inputs/Input/types.js +3 -0
  65. package/dist/components/{Select.svelte → inputs/Select/index.svelte} +3 -4
  66. package/dist/components/inputs/Select/index.svelte.d.ts +3 -0
  67. package/dist/components/inputs/Select/types.d.ts +7 -0
  68. package/dist/components/inputs/Select/types.js +2 -0
  69. package/dist/components/{helper → inputs/helper}/BaseInput.svelte +14 -8
  70. package/dist/components/{helper → inputs/helper}/BaseInput.svelte.d.ts +2 -2
  71. package/dist/components/{helper → inputs/helper}/NumberInput.svelte +8 -7
  72. package/dist/components/{helper → inputs/helper}/NumberInput.svelte.d.ts +1 -2
  73. package/dist/components/inputs/index.d.ts +12 -0
  74. package/dist/components/inputs/index.js +6 -0
  75. package/dist/components/navigation/Breadcrumb/BreadcrumbItem.svelte +37 -0
  76. package/dist/components/navigation/Breadcrumb/BreadcrumbItem.svelte.d.ts +4 -0
  77. package/dist/components/navigation/Breadcrumb/index.stories.svelte +19 -0
  78. package/dist/components/navigation/Breadcrumb/index.stories.svelte.d.ts +18 -0
  79. package/dist/components/navigation/Breadcrumb/index.svelte +21 -0
  80. package/dist/components/navigation/Breadcrumb/index.svelte.d.ts +4 -0
  81. package/dist/components/navigation/Breadcrumb/types.d.ts +6 -0
  82. package/dist/components/{NavbarDropdown.svelte → navigation/Navbar/NavbarDropdown.svelte} +1 -1
  83. package/dist/components/{NavbarDropdown.svelte.d.ts → navigation/Navbar/NavbarDropdown.svelte.d.ts} +1 -1
  84. package/dist/components/{NavbarElement.svelte → navigation/Navbar/NavbarElement.svelte} +2 -2
  85. package/dist/components/{NavbarElement.svelte.d.ts → navigation/Navbar/NavbarElement.svelte.d.ts} +1 -1
  86. package/dist/components/{NavbarSeparator.svelte → navigation/Navbar/NavbarSeparator.svelte} +1 -1
  87. package/dist/components/{NavbarSeparator.svelte.d.ts → navigation/Navbar/NavbarSeparator.svelte.d.ts} +1 -1
  88. package/dist/components/{Navbar.svelte → navigation/Navbar/index.svelte} +1 -2
  89. package/dist/components/navigation/Navbar/index.svelte.d.ts +4 -0
  90. package/dist/{types/Navbar.d.ts → components/navigation/Navbar/types.d.ts} +2 -10
  91. package/dist/{types/Navbar.js → components/navigation/Navbar/types.js} +0 -1
  92. package/dist/components/{SideNavbar.svelte → navigation/SideNavbar/index.svelte} +3 -3
  93. package/dist/components/navigation/SideNavbar/index.svelte.d.ts +4 -0
  94. package/dist/components/navigation/SideNavbar/types.d.ts +10 -0
  95. package/dist/components/navigation/SideNavbar/types.js +2 -0
  96. package/dist/components/navigation/index.d.ts +10 -0
  97. package/dist/components/navigation/index.js +7 -0
  98. package/dist/components/overlay/Tooltip/index.stories.svelte +20 -0
  99. package/dist/components/overlay/Tooltip/index.stories.svelte.d.ts +18 -0
  100. package/dist/components/overlay/Tooltip/index.svelte +193 -0
  101. package/dist/components/overlay/Tooltip/index.svelte.d.ts +4 -0
  102. package/dist/components/overlay/Tooltip/types.d.ts +9 -0
  103. package/dist/components/overlay/Tooltip/types.js +1 -0
  104. package/dist/components/overlay/index.d.ts +2 -0
  105. package/dist/components/overlay/index.js +1 -0
  106. package/dist/components/typography/Text/index.stories.svelte +16 -0
  107. package/dist/components/typography/Text/index.stories.svelte.d.ts +18 -0
  108. package/dist/components/{Text.svelte → typography/Text/index.svelte} +10 -10
  109. package/dist/components/typography/Text/index.svelte.d.ts +4 -0
  110. package/dist/{types/Text.d.ts → components/typography/Text/types.d.ts} +2 -2
  111. package/dist/components/typography/Text/types.js +2 -0
  112. package/dist/components/{Typewriter.svelte → typography/Typewriter/index.svelte} +2 -2
  113. package/dist/components/typography/Typewriter/index.svelte.d.ts +4 -0
  114. package/dist/{types/Typewriter.d.ts → components/typography/Typewriter/types.d.ts} +1 -1
  115. package/dist/components/typography/Typewriter/types.js +2 -0
  116. package/dist/components/typography/index.d.ts +4 -0
  117. package/dist/components/typography/index.js +2 -0
  118. package/dist/components/{Analytics.svelte → utility/Analytics/index.svelte} +1 -1
  119. package/dist/components/utility/Analytics/index.svelte.d.ts +4 -0
  120. package/dist/components/utility/Analytics/types.js +1 -0
  121. package/dist/components/{SEO.svelte → utility/SEO/index.svelte} +1 -1
  122. package/dist/components/utility/SEO/index.svelte.d.ts +4 -0
  123. package/dist/components/utility/SEO/types.js +1 -0
  124. package/dist/components/utility/index.d.ts +4 -0
  125. package/dist/components/utility/index.js +2 -0
  126. package/dist/index.d.ts +8 -25
  127. package/dist/index.js +8 -24
  128. package/dist/styles/color.d.ts +4 -1
  129. package/dist/styles/color.js +175 -166
  130. package/dist/styles/layout.css +38 -2
  131. package/dist/styles/size.d.ts +3 -1
  132. package/dist/styles/size.js +19 -39
  133. package/dist/types/BaseComponent.d.ts +18 -0
  134. package/dist/utils/color.d.ts +16 -0
  135. package/dist/utils/color.js +70 -0
  136. package/package.json +23 -11
  137. package/dist/components/Analytics.svelte.d.ts +0 -4
  138. package/dist/components/Button.svelte +0 -66
  139. package/dist/components/Button.svelte.d.ts +0 -4
  140. package/dist/components/Card.svelte.d.ts +0 -4
  141. package/dist/components/Checkbox.svelte.d.ts +0 -4
  142. package/dist/components/Combobox.svelte.d.ts +0 -4
  143. package/dist/components/FileInput.svelte.d.ts +0 -4
  144. package/dist/components/Input.svelte.d.ts +0 -5
  145. package/dist/components/KBD.svelte.d.ts +0 -3
  146. package/dist/components/Loader.svelte.d.ts +0 -4
  147. package/dist/components/Modal.svelte.d.ts +0 -4
  148. package/dist/components/Navbar.svelte.d.ts +0 -4
  149. package/dist/components/Progress.svelte.d.ts +0 -4
  150. package/dist/components/SEO.svelte.d.ts +0 -4
  151. package/dist/components/Select.svelte.d.ts +0 -4
  152. package/dist/components/SideNavbar.svelte.d.ts +0 -4
  153. package/dist/components/Skeleton.svelte.d.ts +0 -4
  154. package/dist/components/Table.svelte.d.ts +0 -4
  155. package/dist/components/Text.svelte.d.ts +0 -4
  156. package/dist/components/Toast.svelte.d.ts +0 -5
  157. package/dist/components/Toaster.svelte.d.ts +0 -3
  158. package/dist/components/Tooltip.svelte +0 -123
  159. package/dist/components/Tooltip.svelte.d.ts +0 -4
  160. package/dist/components/Typewriter.svelte.d.ts +0 -4
  161. package/dist/types/Input.d.ts +0 -57
  162. package/dist/types/Input.js +0 -5
  163. package/dist/types/Loader.d.ts +0 -5
  164. package/dist/types/Modal.d.ts +0 -6
  165. package/dist/types/Tooltip.d.ts +0 -7
  166. /package/dist/{types/Analytics.js → components/display/Card/types.js} +0 -0
  167. /package/dist/{types/Card.js → components/display/Card/variant.js} +0 -0
  168. /package/dist/components/{KBD.svelte → display/KBD/index.svelte} +0 -0
  169. /package/dist/{types/Button.js → components/display/Skeleton/types.js} +0 -0
  170. /package/dist/{types/Table.js → components/display/Table/types.js} +0 -0
  171. /package/dist/{types/Loader.js → components/feedback/Loader/types.js} +0 -0
  172. /package/dist/{types/Modal.js → components/feedback/Modal/types.js} +0 -0
  173. /package/dist/{types/Checkbox.js → components/feedback/Progress/types.js} +0 -0
  174. /package/dist/{types/SEO.js → components/feedback/Toast/types.js} +0 -0
  175. /package/dist/{types/Skeleton.js → components/inputs/Button/types.js} +0 -0
  176. /package/dist/{types/Progress.js → components/inputs/Checkbox/types.js} +0 -0
  177. /package/dist/{types/Text.js → components/inputs/ColorInput/types.js} +0 -0
  178. /package/dist/{types/Toast.js → components/inputs/Combobox/types.js} +0 -0
  179. /package/dist/{types/Typewriter.js → components/inputs/FileInput/types.js} +0 -0
  180. /package/dist/{types/Tooltip.js → components/navigation/Breadcrumb/types.js} +0 -0
  181. /package/dist/{types/Analytics.d.ts → components/utility/Analytics/types.d.ts} +0 -0
  182. /package/dist/{types/SEO.d.ts → components/utility/SEO/types.d.ts} +0 -0
@@ -0,0 +1,23 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from "@storybook/addon-svelte-csf";
3
+ import ColorInput from "./index.svelte";
4
+ import { colorStyles, colorStyleTypes } from "../../../styles/color.js";
5
+ import { sizeStyles } from "../../../styles/size.js";
6
+
7
+ const { Story } = defineMeta({
8
+ title: "Components/Inputs/ColorInput",
9
+ component: ColorInput,
10
+ argTypes: {
11
+ size: {
12
+ control: "select",
13
+ options: sizeStyles,
14
+ },
15
+ radius: {
16
+ control: "select",
17
+ options: sizeStyles,
18
+ }
19
+ },
20
+ });
21
+ </script>
22
+
23
+ <Story name="Default" args={{ label: "Input Label" }}></Story>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const Index: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type Index = InstanceType<typeof Index>;
18
+ export default Index;
@@ -0,0 +1,384 @@
1
+ <script lang="ts">
2
+ import { getSizeStyle, getSizeStyleClass } from "../../../styles/size.js";
3
+ import type { ColorInputProps } from "./types";
4
+ import { twMerge } from "tailwind-merge";
5
+ import Text from "../../typography/Text/index.svelte";
6
+ import bytes from "bytes";
7
+ import { computePosition, flip, shift, offset, autoUpdate } from "@floating-ui/dom";
8
+
9
+ import Upload from "@lucide/svelte/icons/upload";
10
+ import File from "@lucide/svelte/icons/file";
11
+ import Button from "../Button/index.svelte";
12
+ import { fly } from "svelte/transition";
13
+ import { onMount, tick } from "svelte";
14
+ import { hexToHsl, hexToHsv, hexToRgb, hslToHex } from "../../../utils/color";
15
+ import { Input } from "..";
16
+
17
+ import Pipette from "@lucide/svelte/icons/pipette";
18
+
19
+ let {
20
+ children = undefined,
21
+ class: className = "",
22
+ label = undefined,
23
+ labelClass = "",
24
+ divClass = "",
25
+ outerDivClass = "",
26
+ value = $bindable("#ffffff"),
27
+ required = false,
28
+ disabled = false,
29
+ size = "md",
30
+ radius = "md",
31
+ variant = "full",
32
+ id = crypto.randomUUID(),
33
+ ...restProps
34
+ }: ColorInputProps = $props();
35
+
36
+ let inputElement: HTMLInputElement;
37
+ let dropdownX = $state(0);
38
+ let dropdownY = $state(0);
39
+ let referenceEl = $state<HTMLElement>();
40
+ let floatingEl = $state<HTMLDivElement>();
41
+ let canvasEl = $state<HTMLDivElement>();
42
+ let hueEl = $state<HTMLDivElement>();
43
+ let isOpen = $state(false);
44
+ let referenceWidth = $state(0);
45
+
46
+ let thumbX = $state(0);
47
+ let thumbY = $state(0);
48
+
49
+ let hue = $state(0);
50
+
51
+ const defaultClass = "text-main-text w-full flex items-center gap-1 rounded-full outline-none px-1.5 w-full bg-inherit border-0 py-0";
52
+ const defaultLabelClass = "block text-sub-text font-medium mb-1 pointer-events-none truncate w-fit";
53
+ const defaultDivClass = "relative flex-center gap-0 bg-form-background p-0";
54
+
55
+ const divFullClass = $derived(size === "full" ? "w-full" : "");
56
+ const disabledClass = $derived(disabled ? "opacity-50 pointer-events-none" : "");
57
+
58
+ const combinedOuterDivClass = $derived(twMerge(
59
+ "flex flex-col bg-transparent border-0 p-0",
60
+ divFullClass,
61
+ outerDivClass,
62
+ disabledClass
63
+ ));
64
+ const combinedLabelClass = $derived(twMerge(
65
+ defaultLabelClass,
66
+ getSizeStyleClass(size, "formLabel"),
67
+ labelClass
68
+ ));
69
+ const combinedDivClass = $derived(twMerge(
70
+ defaultDivClass,
71
+ getSizeStyleClass(radius, "radius"),
72
+ divFullClass,
73
+ divClass,
74
+ ));
75
+ let combinedClass = $derived(twMerge(
76
+ getSizeStyleClass(size, "form"),
77
+ defaultClass,
78
+ className,
79
+ ));
80
+
81
+ async function updateDropdownPosition() {
82
+ if (!referenceEl || !floatingEl) return;
83
+
84
+ referenceWidth = referenceEl.offsetWidth;
85
+
86
+ const { x, y } = await computePosition(referenceEl, floatingEl, {
87
+ placement: "bottom-start",
88
+ middleware: [
89
+ offset(8),
90
+ flip(),
91
+ shift({ padding: 8 })
92
+ ]
93
+ });
94
+
95
+ dropdownX = x;
96
+ dropdownY = y;
97
+ }
98
+
99
+ async function handleClick() {
100
+ if (isOpen) {
101
+ isOpen = false;
102
+ } else {
103
+ isOpen = true;
104
+ await tick();
105
+ updateDropdownPosition();
106
+ updateHueAndThumb(hexToHsv(value));
107
+ }
108
+ }
109
+
110
+ async function handleMouseDown(e: MouseEvent) {
111
+ if (
112
+ isOpen &&
113
+ referenceEl && !referenceEl.contains(e.target as Node) &&
114
+ floatingEl && !floatingEl.contains(e.target as Node)
115
+ ) {
116
+ isOpen = false;
117
+ return;
118
+ }
119
+
120
+ if (canvasEl && canvasEl.contains(e.target as Node)) {
121
+ handleMouseMoveCanvas(e);
122
+ const onMove = (e: MouseEvent) => handleMouseMoveCanvas(e);
123
+ const onUp = () => {
124
+ window.removeEventListener("mousemove", onMove);
125
+ window.removeEventListener("mouseup", onUp);
126
+ };
127
+ window.addEventListener("mousemove", onMove);
128
+ window.addEventListener("mouseup", onUp);
129
+ }
130
+ if (hueEl && hueEl.contains(e.target as Node)) {
131
+ handleMouseMoveHue(e);
132
+ const onMove = (e: MouseEvent) => handleMouseMoveHue(e);
133
+ const onUp = () => {
134
+ window.removeEventListener("mousemove", onMove);
135
+ window.removeEventListener("mouseup", onUp);
136
+ };
137
+ window.addEventListener("mousemove", onMove);
138
+ window.addEventListener("mouseup", onUp);
139
+ }
140
+ }
141
+
142
+ async function handleMouseMoveHue(e: MouseEvent) {
143
+ if (!hueEl) return;
144
+
145
+ const rect = hueEl.getBoundingClientRect();
146
+ const y = Math.min(Math.max(e.clientY - rect.top, 0), rect.height);
147
+ hue = (y / rect.height) * 360;
148
+
149
+ const s = thumbX / canvasEl!.offsetWidth;
150
+ const v = 1 - thumbY / canvasEl!.offsetHeight;
151
+
152
+ const l = v * (1 - s / 2);
153
+ const sl = l === 0 || l === 1 ? 0 : (v - l) / Math.min(l, 1 - l);
154
+
155
+ value = hslToHex(hue, sl * 100, l * 100);
156
+ }
157
+
158
+ async function handleMouseMoveCanvas(e: MouseEvent) {
159
+ if (!canvasEl) return;
160
+
161
+ const rect = canvasEl.getBoundingClientRect();
162
+ thumbX = Math.min(Math.max(e.clientX - rect.left, 0), rect.width);
163
+ thumbY = Math.min(Math.max(e.clientY - rect.top, 0), rect.height);
164
+
165
+ const s = thumbX / rect.width;
166
+ const v = 1 - thumbY / rect.height;
167
+
168
+ const l = v * (1 - s / 2);
169
+ const sl = l === 0 || l === 1 ? 0 : (v - l) / Math.min(l, 1 - l);
170
+
171
+ value = hslToHex(hue, sl * 100, l * 100);
172
+ }
173
+
174
+ $effect(() => {
175
+ if (isOpen && referenceEl && floatingEl) {
176
+ const cleanup = autoUpdate(
177
+ referenceEl,
178
+ floatingEl,
179
+ updateDropdownPosition
180
+ );
181
+ return () => cleanup();
182
+ }
183
+ });
184
+
185
+ function handleEnter(e: KeyboardEvent) {
186
+ if (e.key === "Enter")
187
+ (e.target as HTMLInputElement).blur();
188
+ }
189
+
190
+ function onBlurHexCode(e?: FocusEvent) {
191
+ if (!e) return;
192
+ let input = (e.target as HTMLInputElement).value.trim();
193
+ input = input.replace(/^#/, "");
194
+
195
+ if (/^[0-9a-fA-F]{3}$/.test(input))
196
+ input = input.split("").map(c => c + c).join("");
197
+
198
+ if (/^[0-9a-fA-F]{6}$/.test(input))
199
+ value = `#${input}`;
200
+ else
201
+ (e.target as HTMLInputElement).value = value;
202
+
203
+ const hsv = hexToHsv(value);
204
+ updateHueAndThumb(hsv);
205
+ }
206
+
207
+ function onBlueRGB(e?: FocusEvent) {
208
+ if (!e) return;
209
+ const input = (e.target as HTMLInputElement).value.trim();
210
+ const match = input.match(/^(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})$/);
211
+ if (match) {
212
+ const r = Math.min(255, parseInt(match[1]));
213
+ const g = Math.min(255, parseInt(match[2]));
214
+ const b = Math.min(255, parseInt(match[3]));
215
+ value = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
216
+ const hsv = hexToHsv(value);
217
+ updateHueAndThumb(hsv);
218
+ } else {
219
+ (e.target as HTMLInputElement).value = `${hexToRgb(value)?.r}, ${hexToRgb(value)?.g}, ${hexToRgb(value)?.b}`;
220
+ }
221
+ }
222
+
223
+ function onBlurHSV(e?: FocusEvent) {
224
+ if (!e) return;
225
+ const input = (e.target as HTMLInputElement).value.trim();
226
+ const match = input.match(/^(\d+\.?\d*)°?,\s*(\d+\.?\d*)%?,\s*(\d+\.?\d*)%?$/);
227
+ if (match) {
228
+ const h = Math.min(360, parseFloat(match[1]));
229
+ const s = Math.min(100, parseFloat(match[2]));
230
+ const v = Math.min(100, parseFloat(match[3]));
231
+ updateHueAndThumb({h, s, v});
232
+ (e.target as HTMLInputElement).value = `${h.toFixed(2)}°, ${s.toFixed(2)}%, ${v.toFixed(2)}%`;
233
+ } else {
234
+ const hsv = hexToHsv(value);
235
+ (e.target as HTMLInputElement).value = `${hsv.h.toFixed(2)}°, ${hsv.s.toFixed(2)}%, ${hsv.v.toFixed(2)}%`;
236
+ }
237
+ }
238
+
239
+ function onBlurHSL(e?: FocusEvent) {
240
+ if (!e) return;
241
+ const input = (e.target as HTMLInputElement).value.trim();
242
+ const match = input.match(/^(\d{1,3})°?,\s*(\d{1,3})%?,\s*(\d{1,3})%?$/);
243
+ if (match) {
244
+ const h = Math.min(360, parseInt(match[1]));
245
+ const s = Math.min(100, parseInt(match[2]));
246
+ const l = Math.min(100, parseInt(match[3]));
247
+ value = hslToHex(h, s, l);
248
+ const hsv = hexToHsv(value);
249
+ updateHueAndThumb(hsv);
250
+ } else {
251
+ const hsl = hexToHsl(value);
252
+ (e.target as HTMLInputElement).value = `${hsl.h.toFixed(2)}°, ${hsl.s.toFixed(2)}%, ${hsl.l.toFixed(2)}%`;
253
+ }
254
+ }
255
+
256
+ function updateHueAndThumb(hsv: { h: number; s: number; v: number }) {
257
+ hue = hsv.h;
258
+ thumbX = (hsv.s / 100) * canvasEl!.offsetWidth;
259
+ thumbY = (1 - hsv.v / 100) * canvasEl!.offsetHeight;
260
+ }
261
+ </script>
262
+
263
+ <svelte:window onmousedown={handleMouseDown}/>
264
+
265
+ <div class={combinedOuterDivClass} bind:this={referenceEl}>
266
+ <Text tag="label" for={id} class={combinedLabelClass} style={getSizeStyle(size, "formLabel")}>
267
+ {label}
268
+ {#if required}
269
+ <span class="text-[#E05555]">*</span>
270
+ {/if}
271
+ </Text>
272
+ <Button color="none" class={combinedDivClass} onclick={handleClick} {disabled}>
273
+ <input
274
+ {id}
275
+ bind:value={value}
276
+ bind:this={inputElement}
277
+ class="hidden"
278
+ {required}
279
+ {disabled}
280
+ aria-disabled={disabled}
281
+ {...restProps}
282
+ type="color"
283
+ />
284
+
285
+ <div class="rounded-full border border-form-hover ml-2 w-6 h-6" style={`background-color: ${value || 'transparent'}`}></div>
286
+
287
+ <Text class={combinedClass}>
288
+ {value ? value : "Select color"}
289
+ </Text>
290
+ </Button>
291
+
292
+ {#if isOpen}
293
+ {@const rgb = hexToRgb(value || "#000000")}
294
+ {@const hsl = hexToHsl(value || "#000000")}
295
+ {@const hsv = hexToHsv(value || "#000000")}
296
+ <div
297
+ bind:this={floatingEl}
298
+ transition:fly={{ y: -10, duration: 200 }}
299
+ class="fixed z-999999 bg-sub-background rounded-md p-4 flex gap-2.5 flex-wrap"
300
+ style="top: {dropdownY}px; left: {dropdownX}px; min-width: {referenceWidth}px; max-width: calc(100vw - 16px);"
301
+ >
302
+ <div class="color-canvas relative rounded h-36 cursor-crosshair" style="background-color: hsl({hue}, 100%, 50%);" bind:this={canvasEl}>
303
+ <div
304
+ class="absolute w-3 h-3 rounded-full border border-white shadow thumb pointer-events-none"
305
+ style="background-color: {value || 'transparent'}; left: {thumbX}px; top: {thumbY}px;"
306
+ ></div>
307
+ </div>
308
+
309
+ <div class="h-36 w-4 hue-slider relative" bind:this={hueEl}>
310
+ <div class="slider absolute w-5 h-1 border border-white shadow" style="top: {(hue / 360) * 100}%"></div>
311
+ </div>
312
+
313
+ <div class="grow flex flex-col gap-2 min-w-[160px]">
314
+ <Input
315
+ type="text"
316
+ variant="floating"
317
+ label="Hex"
318
+ size="sm"
319
+ value={value}
320
+ onblur={onBlurHexCode}
321
+ onkeydown={handleEnter}
322
+ placeholder="#ffffff"
323
+ />
324
+
325
+ <Input
326
+ type="text"
327
+ variant="floating"
328
+ label="RGB"
329
+ size="sm"
330
+ value="{rgb.r}, {rgb.g}, {rgb.b}"
331
+ onblur={onBlueRGB}
332
+ onkeydown={handleEnter}
333
+ placeholder="0, 0, 0"
334
+ />
335
+
336
+ <Input
337
+ type="text"
338
+ variant="floating"
339
+ label="HSV"
340
+ size="sm"
341
+ value="{hue.toFixed(2)}°, {hsv.s.toFixed(2)}%, {hsv.v.toFixed(2)}%"
342
+ onblur={onBlurHSV}
343
+ onkeydown={handleEnter}
344
+ placeholder="0, 0, 0"
345
+ />
346
+
347
+ <Input
348
+ type="text"
349
+ variant="floating"
350
+ label="HSL"
351
+ size="sm"
352
+ value="{hue.toFixed(2)}°, {hsl.s.toFixed(2)}%, {hsl.l.toFixed(2)}%"
353
+ onblur={onBlurHSL}
354
+ onkeydown={handleEnter}
355
+ placeholder="0, 0, 0"
356
+ />
357
+ </div>
358
+ </div>
359
+ {/if}
360
+ </div>
361
+
362
+ <style>
363
+ .color-canvas {
364
+ aspect-ratio: 1 / 0.75;
365
+ padding: 10;
366
+ background-color: hsl(0, 100%, 50%);
367
+ background-image:
368
+ linear-gradient(to bottom, transparent, #000),
369
+ linear-gradient(to right, #fff, transparent);
370
+ cursor: crosshair;
371
+ }
372
+
373
+ .hue-slider {
374
+ background: linear-gradient(to bottom, red, yellow, lime, cyan, blue, magenta, red);
375
+ }
376
+
377
+ .thumb {
378
+ transform: translate(-50%, -50%);
379
+ }
380
+
381
+ .slider {
382
+ transform: translate(-10%, -50%);
383
+ }
384
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { ColorInputProps } from "./types";
2
+ declare const Index: import("svelte").Component<ColorInputProps, {}, "value">;
3
+ type Index = ReturnType<typeof Index>;
4
+ export default Index;
@@ -0,0 +1,16 @@
1
+ import type { SizeStyle } from "$styles/size";
2
+ import type { BaseComponentProps } from "$types/BaseComponent";
3
+ export type ColorInputVariant = "full" | "compact" | "input";
4
+ export interface ColorInputProps extends BaseComponentProps {
5
+ label?: string;
6
+ labelClass?: string;
7
+ divClass?: string;
8
+ outerDivClass?: string;
9
+ value?: string;
10
+ required?: boolean;
11
+ disabled?: boolean;
12
+ variant?: ColorInputVariant;
13
+ size?: SizeStyle;
14
+ radius?: SizeStyle;
15
+ id?: `${string}-${string}-${string}-${string}-${string}`;
16
+ }
@@ -0,0 +1,34 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from "@storybook/addon-svelte-csf";
3
+ import Combobox from "./index.svelte";
4
+ import { colorStyles, colorStyleTypes } from "../../../styles/color.js";
5
+ import { sizeStyles } from "../../../styles/size.js";
6
+
7
+ const { Story } = defineMeta({
8
+ title: "Components/Inputs/Combobox",
9
+ component: Combobox,
10
+ argTypes: {
11
+ size: {
12
+ control: "select",
13
+ options: sizeStyles,
14
+ },
15
+ radius: {
16
+ control: "select",
17
+ options: sizeStyles,
18
+ },
19
+ },
20
+ });
21
+ </script>
22
+
23
+ <Story name="Default" args={{ label: "Combobox", placeholder: "Select an option...", options: [
24
+ "USA",
25
+ "Canada",
26
+ "Mexico",
27
+ "United Kingdom",
28
+ "Germany",
29
+ "France",
30
+ "Italy",
31
+ "Spain",
32
+ "Australia",
33
+ "Japan"
34
+ ] }}></Story>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const Index: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type Index = InstanceType<typeof Index>;
18
+ export default Index;
@@ -1,12 +1,14 @@
1
1
  <script lang="ts">
2
- import type { ComboboxProps, InputProps } from "../types/Input.js";
2
+ import type { ComboboxProps } from "./types";
3
3
  import { twMerge } from "tailwind-merge";
4
- import { getSizeStyleClass } from "../styles/size.js";
4
+ import { getSizeStyleClass } from "$styles/size";
5
+ import { computePosition, flip, shift, offset, autoUpdate } from "@floating-ui/dom";
5
6
 
6
- import BaseInput from "./helper/BaseInput.svelte";
7
- import Text from "./Text.svelte";
7
+ import BaseInput from "../helper/BaseInput.svelte";
8
+ import Text from "../../typography/Text/index.svelte";
8
9
  import { fly } from "svelte/transition";
9
- import { debounce } from "../utils/debounce.js";
10
+ import { debounce } from "../../../utils/debounce.js";
11
+ import { tick } from "svelte";
10
12
 
11
13
  let {
12
14
  children = undefined,
@@ -31,8 +33,14 @@
31
33
  ...restProps
32
34
  }: ComboboxProps = $props();
33
35
 
36
+ let isFocused = $state(false);
34
37
  let activeIndex = $state(0);
35
38
 
39
+ let dropdownX = $state(0);
40
+ let dropdownY = $state(0);
41
+ let referenceEl = $state<HTMLElement>();
42
+ let floatingEl = $state<HTMLDivElement>();
43
+
36
44
  let debouncedSearch = $state("");
37
45
 
38
46
  const updateSearch = debounce((v: string) => {
@@ -60,8 +68,6 @@
60
68
  return styles.join("; ");
61
69
  });
62
70
 
63
- let isFocused = $state(false);
64
-
65
71
  function handleFocus(e: FocusEvent) {
66
72
  isFocused = true;
67
73
  onfocus?.(e);
@@ -74,12 +80,32 @@
74
80
  onblur?.(e);
75
81
  }
76
82
 
83
+ async function updateDropdownPosition() {
84
+ if (!referenceEl || !floatingEl) return;
85
+
86
+ referenceWidth = referenceEl.offsetWidth;
87
+
88
+ const { x, y } = await computePosition(referenceEl, floatingEl, {
89
+ placement: "bottom-start",
90
+ middleware: [
91
+ offset(8),
92
+ flip(),
93
+ shift({ padding: 8 })
94
+ ]
95
+ });
96
+
97
+ dropdownX = x;
98
+ dropdownY = y;
99
+ }
100
+
77
101
  let defaultClass = "text-main-text w-full outline-none px-1.5 w-full bg-inherit border-0 focus:ring-0 focus-visible:ring-0 rounded-none";
78
102
 
79
103
  let defaultInputClassCheck = $derived(variant !== "floating" ? "py-0" : "");
80
104
  let combinedClass = $derived(twMerge(defaultClass, sizeClasses, defaultInputClassCheck, labelSizeClass, className));
81
105
  let combinedDivClass = $derived(twMerge(divClass));
82
106
 
107
+ let referenceWidth = $state(0);
108
+
83
109
  function onClickItem(event: MouseEvent, option: string) {
84
110
  event.preventDefault();
85
111
  value = option;
@@ -145,8 +171,32 @@
145
171
  }
146
172
 
147
173
  let optionsContainerElement = $state<HTMLDivElement>();
174
+
175
+ $effect(() => {
176
+ if (isFocused && referenceEl && floatingEl) {
177
+ const cleanup = autoUpdate(
178
+ referenceEl,
179
+ floatingEl,
180
+ updateDropdownPosition
181
+ );
182
+ return () => cleanup();
183
+ }
184
+ });
185
+
186
+ function handleMouseDown(e: MouseEvent) {
187
+ if (
188
+ isFocused &&
189
+ referenceEl && !referenceEl.contains(e.target as Node) &&
190
+ floatingEl && !floatingEl.contains(e.target as Node)
191
+ ) {
192
+ isFocused = false;
193
+ return;
194
+ }
195
+ }
148
196
  </script>
149
197
 
198
+ <svelte:window onmousedown={handleMouseDown}/>
199
+
150
200
  <BaseInput
151
201
  {children}
152
202
  {className}
@@ -161,7 +211,9 @@
161
211
  {size}
162
212
  {radius}
163
213
  {id}
164
- {icon}>
214
+ {isFocused}
215
+ {icon}
216
+ bind:wrapper={referenceEl}>
165
217
 
166
218
  {#snippet innerDivElement()}
167
219
  <input
@@ -183,8 +235,13 @@
183
235
 
184
236
  {#snippet outerDivElementAfter()}
185
237
  {#if isFocused}
186
- <div transition:fly={{ y: -10, duration: 200 }} class="absolute z-100 top-full left-0 right-0 mt-2 border-2 overflow-hidden border-blue-500 bg-sub-background {getSizeStyleClass(radius, "radius")}">
187
- {#if totalMatches > limit}
238
+ <div
239
+ bind:this={floatingEl}
240
+ transition:fly={{ y: -10, duration: 200 }}
241
+ style="position: fixed; top: {dropdownY}px; left: {dropdownX}px; width: {referenceWidth}px;"
242
+ class="z-999999 border-2 overflow-hidden border-blue-500 bg-sub-background {getSizeStyleClass(radius, 'radius')}"
243
+ >
244
+ {#if totalMatches > 0 && options.length > limit}
188
245
  <Text class="text-xs py-0.5 px-1 text-sub-text italic sticky top-0 bg-sub-background w-full">
189
246
  Showing {limit} of {totalMatches} results for "{value}"
190
247
  </Text>
@@ -0,0 +1,4 @@
1
+ import type { ComboboxProps } from "./types";
2
+ declare const Index: import("svelte").Component<ComboboxProps, {}, "value">;
3
+ type Index = ReturnType<typeof Index>;
4
+ export default Index;
@@ -0,0 +1,8 @@
1
+ import type { BaseInputProps } from "$types/BaseComponent";
2
+ export interface ComboboxProps extends BaseInputProps {
3
+ options?: string[];
4
+ placeholder?: string;
5
+ onfocus?: (e?: FocusEvent) => void;
6
+ onblur?: (e?: FocusEvent) => void;
7
+ limit?: number;
8
+ }
@@ -1,13 +1,13 @@
1
1
  <script lang="ts">
2
- import { getSizeStyle, getSizeStyleClass } from "../styles/size.js";
3
- import type { FileInputProps } from "../types/Input.js";
2
+ import { getSizeStyle, getSizeStyleClass } from "../../../styles/size.js";
3
+ import type { FileInputProps } from "./types";
4
4
  import { twMerge } from "tailwind-merge";
5
- import Text from "./Text.svelte";
5
+ import Text from "../../typography/Text/index.svelte";
6
6
  import bytes from "bytes";
7
7
 
8
8
  import Upload from "@lucide/svelte/icons/upload";
9
9
  import File from "@lucide/svelte/icons/file";
10
- import Button from "./Button.svelte";
10
+ import Button from "../Button/index.svelte";
11
11
 
12
12
  let {
13
13
  children = undefined,
@@ -0,0 +1,4 @@
1
+ import type { FileInputProps } from "./types";
2
+ declare const Index: import("svelte").Component<FileInputProps, {}, "files">;
3
+ type Index = ReturnType<typeof Index>;
4
+ export default Index;