@ims360/svelte-ivory 0.1.15 → 0.2.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 (176) hide show
  1. package/README.md +0 -5
  2. package/dist/components/ai/AiMessage.svelte +2 -3
  3. package/dist/components/ai/AiMessage.svelte.d.ts.map +1 -1
  4. package/dist/components/ai/Chat.svelte +2 -3
  5. package/dist/components/ai/Chat.svelte.d.ts.map +1 -1
  6. package/dist/components/ai/Markdown.svelte +4 -7
  7. package/dist/components/ai/Markdown.svelte.d.ts.map +1 -1
  8. package/dist/components/ai/UserMessage.svelte +2 -3
  9. package/dist/components/ai/UserMessage.svelte.d.ts.map +1 -1
  10. package/dist/components/basic/checkbox/Checkbox.svelte +13 -11
  11. package/dist/components/basic/checkbox/Checkbox.svelte.d.ts.map +1 -1
  12. package/dist/components/basic/toggle/Toggle.svelte +5 -8
  13. package/dist/components/basic/toggle/Toggle.svelte.d.ts.map +1 -1
  14. package/dist/components/inputs/CheckboxInput.svelte +39 -0
  15. package/dist/components/inputs/CheckboxInput.svelte.d.ts +12 -0
  16. package/dist/components/inputs/CheckboxInput.svelte.d.ts.map +1 -0
  17. package/dist/components/inputs/ColorInput.svelte +25 -0
  18. package/dist/components/inputs/ColorInput.svelte.d.ts +5 -0
  19. package/dist/components/inputs/ColorInput.svelte.d.ts.map +1 -0
  20. package/dist/components/inputs/DateInput.svelte +11 -0
  21. package/dist/components/inputs/DateInput.svelte.d.ts +5 -0
  22. package/dist/components/inputs/DateInput.svelte.d.ts.map +1 -0
  23. package/dist/components/inputs/EmailInput.svelte +16 -0
  24. package/dist/components/inputs/EmailInput.svelte.d.ts +5 -0
  25. package/dist/components/inputs/EmailInput.svelte.d.ts.map +1 -0
  26. package/dist/components/inputs/FileInput.svelte +131 -0
  27. package/dist/components/inputs/FileInput.svelte.d.ts +11 -0
  28. package/dist/components/inputs/FileInput.svelte.d.ts.map +1 -0
  29. package/dist/components/inputs/Input.svelte +101 -0
  30. package/dist/components/inputs/Input.svelte.d.ts +48 -0
  31. package/dist/components/inputs/Input.svelte.d.ts.map +1 -0
  32. package/dist/components/inputs/NumberInput.svelte +17 -0
  33. package/dist/components/inputs/NumberInput.svelte.d.ts +10 -0
  34. package/dist/components/inputs/NumberInput.svelte.d.ts.map +1 -0
  35. package/dist/components/inputs/PasswordCreateInput.svelte +60 -0
  36. package/dist/components/inputs/PasswordCreateInput.svelte.d.ts +12 -0
  37. package/dist/components/inputs/PasswordCreateInput.svelte.d.ts.map +1 -0
  38. package/dist/components/inputs/PasswordInput.svelte +27 -0
  39. package/dist/components/inputs/PasswordInput.svelte.d.ts +10 -0
  40. package/dist/components/inputs/PasswordInput.svelte.d.ts.map +1 -0
  41. package/dist/components/inputs/TextInput.svelte +16 -0
  42. package/dist/components/inputs/TextInput.svelte.d.ts +7 -0
  43. package/dist/components/inputs/TextInput.svelte.d.ts.map +1 -0
  44. package/dist/components/inputs/ToggleInput.svelte +24 -0
  45. package/dist/components/inputs/ToggleInput.svelte.d.ts +5 -0
  46. package/dist/components/inputs/ToggleInput.svelte.d.ts.map +1 -0
  47. package/dist/components/inputs/index.d.ts +13 -0
  48. package/dist/components/inputs/index.d.ts.map +1 -1
  49. package/dist/components/inputs/index.js +13 -0
  50. package/dist/components/inputs/issues/FormIssues.svelte +35 -0
  51. package/dist/components/inputs/issues/FormIssues.svelte.d.ts +10 -0
  52. package/dist/components/inputs/issues/FormIssues.svelte.d.ts.map +1 -0
  53. package/dist/components/inputs/select/Select.svelte +69 -0
  54. package/dist/components/inputs/select/Select.svelte.d.ts +15 -0
  55. package/dist/components/inputs/select/Select.svelte.d.ts.map +1 -0
  56. package/dist/components/inputs/select/SelectOption.svelte +34 -0
  57. package/dist/components/inputs/select/SelectOption.svelte.d.ts +11 -0
  58. package/dist/components/inputs/select/SelectOption.svelte.d.ts.map +1 -0
  59. package/dist/components/layout/Heading.svelte +5 -4
  60. package/dist/components/layout/Heading.svelte.d.ts.map +1 -1
  61. package/dist/components/layout/dialog/Dialog.svelte +5 -8
  62. package/dist/components/layout/dialog/Dialog.svelte.d.ts.map +1 -1
  63. package/dist/components/layout/drawer/Drawer.svelte +7 -10
  64. package/dist/components/layout/drawer/Drawer.svelte.d.ts.map +1 -1
  65. package/dist/components/layout/index.d.ts +1 -2
  66. package/dist/components/layout/index.d.ts.map +1 -1
  67. package/dist/components/layout/index.js +0 -1
  68. package/dist/components/layout/modal/Modal.svelte +10 -15
  69. package/dist/components/layout/modal/Modal.svelte.d.ts.map +1 -1
  70. package/dist/components/layout/popover/Popover.svelte +96 -79
  71. package/dist/components/layout/popover/Popover.svelte.d.ts +8 -22
  72. package/dist/components/layout/popover/Popover.svelte.d.ts.map +1 -1
  73. package/dist/components/layout/tabs/Tab.svelte +5 -8
  74. package/dist/components/layout/tabs/Tab.svelte.d.ts.map +1 -1
  75. package/dist/components/layout/tooltip/Tooltip.svelte +4 -7
  76. package/dist/components/layout/tooltip/Tooltip.svelte.d.ts.map +1 -1
  77. package/dist/components/table/Column.svelte +7 -10
  78. package/dist/components/table/Column.svelte.d.ts.map +1 -1
  79. package/dist/components/table/ColumnHead.svelte +2 -2
  80. package/dist/components/table/Table.svelte +5 -8
  81. package/dist/components/table/Table.svelte.d.ts.map +1 -1
  82. package/dist/components/table/VirtualList.svelte +3 -8
  83. package/dist/components/table/VirtualList.svelte.d.ts.map +1 -1
  84. package/dist/theme.svelte.d.ts +18 -0
  85. package/dist/theme.svelte.d.ts.map +1 -1
  86. package/dist/types.d.ts.map +1 -1
  87. package/dist/utils/{actions → attachments}/clickOutside.d.ts +2 -4
  88. package/dist/utils/attachments/clickOutside.d.ts.map +1 -0
  89. package/dist/utils/{actions → attachments}/clickOutside.js +4 -7
  90. package/dist/utils/attachments/focusTrap.d.ts +3 -0
  91. package/dist/utils/attachments/focusTrap.d.ts.map +1 -0
  92. package/dist/utils/{actions → attachments}/focusTrap.js +5 -14
  93. package/dist/utils/{actions → attachments}/index.d.ts +0 -1
  94. package/dist/utils/attachments/index.d.ts.map +1 -0
  95. package/dist/utils/{actions → attachments}/index.js +0 -1
  96. package/dist/utils/attachments/resize.d.ts +6 -0
  97. package/dist/utils/attachments/resize.d.ts.map +1 -0
  98. package/dist/utils/{actions → attachments}/resize.js +3 -5
  99. package/dist/utils/attachments/shortcut.svelte.d.ts.map +1 -0
  100. package/dist/utils/attachments/visible.d.ts +6 -0
  101. package/dist/utils/attachments/visible.d.ts.map +1 -0
  102. package/dist/utils/attachments/visible.js +12 -0
  103. package/dist/utils/merge.d.ts +4 -0
  104. package/dist/utils/merge.d.ts.map +1 -0
  105. package/dist/utils/merge.js +6 -0
  106. package/package.json +7 -5
  107. package/src/lib/components/ai/AiMessage.svelte +2 -3
  108. package/src/lib/components/ai/Chat.svelte +2 -3
  109. package/src/lib/components/ai/Markdown.svelte +4 -7
  110. package/src/lib/components/ai/UserMessage.svelte +2 -3
  111. package/src/lib/components/basic/checkbox/Checkbox.svelte +13 -11
  112. package/src/lib/components/basic/toggle/Toggle.svelte +5 -8
  113. package/src/lib/components/inputs/CheckboxInput.svelte +39 -0
  114. package/src/lib/components/inputs/ColorInput.svelte +25 -0
  115. package/src/lib/components/inputs/DateInput.svelte +11 -0
  116. package/src/lib/components/inputs/EmailInput.svelte +16 -0
  117. package/src/lib/components/inputs/FileInput.svelte +131 -0
  118. package/src/lib/components/inputs/Input.svelte +101 -0
  119. package/src/lib/components/inputs/NumberInput.svelte +17 -0
  120. package/src/lib/components/inputs/PasswordCreateInput.svelte +60 -0
  121. package/src/lib/components/inputs/PasswordInput.svelte +27 -0
  122. package/src/lib/components/inputs/TextInput.svelte +16 -0
  123. package/src/lib/components/inputs/ToggleInput.svelte +24 -0
  124. package/src/lib/components/inputs/index.ts +17 -0
  125. package/src/lib/components/inputs/issues/FormIssues.svelte +35 -0
  126. package/src/lib/components/inputs/select/Select.svelte +69 -0
  127. package/src/lib/components/inputs/select/SelectOption.svelte +34 -0
  128. package/src/lib/components/layout/Heading.svelte +5 -4
  129. package/src/lib/components/layout/dialog/Dialog.svelte +5 -8
  130. package/src/lib/components/layout/drawer/Drawer.svelte +7 -10
  131. package/src/lib/components/layout/index.ts +1 -5
  132. package/src/lib/components/layout/modal/Modal.svelte +10 -15
  133. package/src/lib/components/layout/popover/Popover.svelte +96 -79
  134. package/src/lib/components/layout/tabs/Tab.svelte +5 -8
  135. package/src/lib/components/layout/tooltip/Tooltip.svelte +4 -7
  136. package/src/lib/components/table/Column.svelte +7 -10
  137. package/src/lib/components/table/ColumnHead.svelte +2 -2
  138. package/src/lib/components/table/Table.svelte +5 -8
  139. package/src/lib/components/table/VirtualList.svelte +3 -8
  140. package/src/lib/theme.svelte.ts +18 -0
  141. package/src/lib/types.ts +3 -2
  142. package/src/lib/utils/attachments/clickOutside.ts +36 -0
  143. package/src/lib/utils/attachments/focusTrap.ts +67 -0
  144. package/src/lib/utils/{actions → attachments}/index.ts +0 -1
  145. package/src/lib/utils/attachments/resize.ts +38 -0
  146. package/src/lib/utils/attachments/visible.ts +22 -0
  147. package/src/lib/utils/merge.ts +7 -0
  148. package/dist/components/layout/portal/Portal.svelte +0 -39
  149. package/dist/components/layout/portal/Portal.svelte.d.ts +0 -21
  150. package/dist/components/layout/portal/Portal.svelte.d.ts.map +0 -1
  151. package/dist/components/layout/portal/index.d.ts +0 -6
  152. package/dist/components/layout/portal/index.d.ts.map +0 -1
  153. package/dist/components/layout/portal/index.js +0 -5
  154. package/dist/utils/actions/clickOutside.d.ts.map +0 -1
  155. package/dist/utils/actions/focusTrap.d.ts +0 -5
  156. package/dist/utils/actions/focusTrap.d.ts.map +0 -1
  157. package/dist/utils/actions/index.d.ts.map +0 -1
  158. package/dist/utils/actions/portal.d.ts +0 -10
  159. package/dist/utils/actions/portal.d.ts.map +0 -1
  160. package/dist/utils/actions/portal.js +0 -39
  161. package/dist/utils/actions/resize.d.ts +0 -6
  162. package/dist/utils/actions/resize.d.ts.map +0 -1
  163. package/dist/utils/actions/shortcut.svelte.d.ts.map +0 -1
  164. package/dist/utils/actions/visible.d.ts +0 -6
  165. package/dist/utils/actions/visible.d.ts.map +0 -1
  166. package/dist/utils/actions/visible.js +0 -14
  167. package/src/lib/components/layout/portal/Portal.svelte +0 -39
  168. package/src/lib/components/layout/portal/index.ts +0 -7
  169. package/src/lib/utils/actions/clickOutside.ts +0 -38
  170. package/src/lib/utils/actions/focusTrap.ts +0 -65
  171. package/src/lib/utils/actions/portal.ts +0 -43
  172. package/src/lib/utils/actions/resize.ts +0 -35
  173. package/src/lib/utils/actions/visible.ts +0 -28
  174. /package/dist/utils/{actions → attachments}/shortcut.svelte.d.ts +0 -0
  175. /package/dist/utils/{actions → attachments}/shortcut.svelte.js +0 -0
  176. /package/src/lib/utils/{actions → attachments}/shortcut.svelte.ts +0 -0
@@ -1,5 +1,4 @@
1
- /** Dispatch event on click outside of node */
2
- export function clickOutside(node, params) {
1
+ export const clickOutside = (params) => (node) => {
3
2
  function handleClick(event) {
4
3
  if (!(event.target instanceof Node) ||
5
4
  !node ||
@@ -15,9 +14,7 @@ export function clickOutside(node, params) {
15
14
  params.callback(event);
16
15
  }
17
16
  document.addEventListener('click', handleClick, true);
18
- return {
19
- destroy() {
20
- document.removeEventListener('click', handleClick, true);
21
- }
17
+ return () => {
18
+ document.removeEventListener('click', handleClick, true);
22
19
  };
23
- }
20
+ };
@@ -0,0 +1,3 @@
1
+ import type { Attachment } from 'svelte/attachments';
2
+ export declare const focusTrap: (enabled: boolean) => Attachment;
3
+ //# sourceMappingURL=focusTrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focusTrap.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/attachments/focusTrap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,eAAO,MAAM,SAAS,GACjB,SAAS,OAAO,KAAG,UA+DnB,CAAC"}
@@ -1,4 +1,4 @@
1
- export function focusTrap(node, enabled) {
1
+ export const focusTrap = (enabled) => (node) => {
2
2
  const elementWhitelist = 'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])';
3
3
  let firstElement;
4
4
  let lastElement;
@@ -48,17 +48,8 @@ export function focusTrap(node, enabled) {
48
48
  const observer = new MutationObserver(onObservationChange);
49
49
  observer.observe(node, { childList: true, subtree: true });
50
50
  // Lifecycle
51
- return {
52
- update(newArgs) {
53
- enabled = newArgs;
54
- if (newArgs)
55
- scanElements(false);
56
- else
57
- cleanUp();
58
- },
59
- destroy() {
60
- cleanUp();
61
- observer.disconnect();
62
- }
51
+ return () => {
52
+ cleanUp();
53
+ observer.disconnect();
63
54
  };
64
- }
55
+ };
@@ -1,6 +1,5 @@
1
1
  export { clickOutside } from './clickOutside';
2
2
  export { focusTrap } from './focusTrap';
3
- export { portal } from './portal';
4
3
  export { resize } from './resize';
5
4
  export { shortcut } from './shortcut.svelte';
6
5
  export { onFirstVisible } from './visible';
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/attachments/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
@@ -1,6 +1,5 @@
1
1
  export { clickOutside } from './clickOutside';
2
2
  export { focusTrap } from './focusTrap';
3
- export { portal } from './portal';
4
3
  export { resize } from './resize';
5
4
  export { shortcut } from './shortcut.svelte';
6
5
  export { onFirstVisible } from './visible';
@@ -0,0 +1,6 @@
1
+ import type { Attachment } from 'svelte/attachments';
2
+ export declare const resize: ({ resized, dragging }: {
3
+ resized: (mouseX: number) => void;
4
+ dragging: (dragging: boolean) => void;
5
+ }) => Attachment;
6
+ //# sourceMappingURL=resize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resize.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/attachments/resize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,eAAO,MAAM,MAAM,GACd,uBAGE;IACC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC,KAAG,UA4BH,CAAC"}
@@ -1,4 +1,4 @@
1
- export const resize = (node, { resized, dragging }) => {
1
+ export const resize = ({ resized, dragging }) => (node) => {
2
2
  const onResize = (e) => {
3
3
  resized(e.clientX);
4
4
  };
@@ -17,9 +17,7 @@ export const resize = (node, { resized, dragging }) => {
17
17
  document.getElementsByTagName('html')[0].style.userSelect = 'none';
18
18
  };
19
19
  node.addEventListener('pointerdown', onResizeStart);
20
- return {
21
- destroy() {
22
- node.removeEventListener('pointerdown', onResizeStart);
23
- }
20
+ return () => {
21
+ node.removeEventListener('pointerdown', onResizeStart);
24
22
  };
25
23
  };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shortcut.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/attachments/shortcut.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,MAAM,WAAW,cAAc;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AA4CD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,UAAU,CAQ3D"}
@@ -0,0 +1,6 @@
1
+ import type { Attachment } from 'svelte/attachments';
2
+ export declare const onFirstVisible: (params: {
3
+ callback: () => void;
4
+ options?: Partial<IntersectionObserverInit>;
5
+ }) => Attachment;
6
+ //# sourceMappingURL=visible.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visible.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/attachments/visible.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,eAAO,MAAM,cAAc,GACtB,QAAQ;IAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,CAAA;CAAE,KAAG,UAkBhF,CAAC"}
@@ -0,0 +1,12 @@
1
+ export const onFirstVisible = (params) => (node) => {
2
+ const observer = new IntersectionObserver((e) => {
3
+ if (!e[0].isIntersecting) {
4
+ return;
5
+ }
6
+ params.callback();
7
+ }, { root: null, threshold: 1, ...params.options });
8
+ observer.observe(node);
9
+ return () => {
10
+ observer.disconnect();
11
+ };
12
+ };
@@ -0,0 +1,4 @@
1
+ import { type ClassArray } from 'clsx';
2
+ /** `twMerge(clsx(classes))` */
3
+ export declare const merge: (...classes: ClassArray) => string;
4
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/lib/utils/merge.ts"],"names":[],"mappings":"AAAA,OAAa,EAAE,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAG7C,+BAA+B;AAC/B,eAAO,MAAM,KAAK,GAAI,GAAG,SAAS,UAAU,WAE3C,CAAC"}
@@ -0,0 +1,6 @@
1
+ import clsx, {} from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ /** `twMerge(clsx(classes))` */
4
+ export const merge = (...classes) => {
5
+ return twMerge(clsx(classes));
6
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ims360/svelte-ivory",
3
- "version": "0.1.15",
3
+ "version": "0.2.0",
4
4
  "keywords": [
5
5
  "svelte"
6
6
  ],
@@ -38,9 +38,9 @@
38
38
  "types": "./dist/components/inputs/index.d.ts",
39
39
  "svelte": "./dist/components/inputs/index.js"
40
40
  },
41
- "./utils/actions": {
42
- "types": "./dist/utils/actions/index.d.ts",
43
- "svelte": "./dist/utils/actions/index.js"
41
+ "./utils/attachments": {
42
+ "types": "./dist/utils/attachments/index.d.ts",
43
+ "svelte": "./dist/utils/attachments/index.js"
44
44
  },
45
45
  "./utils/functions": {
46
46
  "types": "./dist/utils/functions/index.d.ts",
@@ -73,6 +73,7 @@
73
73
  "dependencies": {
74
74
  "@floating-ui/dom": "^1.7.4",
75
75
  "@lucide/svelte": "^0.553.0",
76
+ "@oddbird/css-anchor-positioning": "^0.8.0",
76
77
  "@tailwindcss/forms": "^0.5.10",
77
78
  "@tailwindcss/typography": "^0.5.19",
78
79
  "@tailwindcss/vite": "^4.1.17",
@@ -108,7 +109,8 @@
108
109
  "typescript": "^5.9.3",
109
110
  "typescript-eslint": "^8.46.4",
110
111
  "vite": "^7.2.2",
111
- "vitest": "^4.0.8"
112
+ "vitest": "^4.0.8",
113
+ "zod": "^4.1.13"
112
114
  },
113
115
  "peerDependencies": {
114
116
  "@skeletonlabs/skeleton": "^3.1.1",
@@ -5,11 +5,10 @@
5
5
  -->
6
6
 
7
7
  <script lang="ts">
8
+ import { merge } from '$lib/utils/merge';
8
9
  import { ThumbsDown, ThumbsUp } from '@lucide/svelte';
9
- import clsx from 'clsx';
10
10
  import type { Snippet } from 'svelte';
11
11
  import type { ClassValue } from 'svelte/elements';
12
- import { twMerge } from 'tailwind-merge';
13
12
  import CopyToClipboardButton from '../buttons/CopyToClipboardButton.svelte';
14
13
  import type { AiChatMessage } from './Chat.svelte';
15
14
  import Markdown from './Markdown.svelte';
@@ -48,7 +47,7 @@
48
47
  </script>
49
48
 
50
49
  <div
51
- class={twMerge(clsx('group flex w-full flex-col items-start', clazz))}
50
+ class={merge('group flex w-full flex-col items-start', clazz)}
52
51
  style={minHeight ? `min-height: ${minHeight}px;` : undefined}
53
52
  >
54
53
  {@render messageText({
@@ -6,10 +6,9 @@
6
6
  -->
7
7
 
8
8
  <script lang="ts" module>
9
- import clsx from 'clsx';
9
+ import { merge } from '$lib/utils/merge';
10
10
  import { tick, type Snippet } from 'svelte';
11
11
  import type { ClassValue } from 'svelte/elements';
12
- import { twMerge } from 'tailwind-merge';
13
12
  import AiMessage from './AiMessage.svelte';
14
13
  import UserMessage from './UserMessage.svelte';
15
14
 
@@ -101,7 +100,7 @@
101
100
  }
102
101
  </script>
103
102
 
104
- <div class={twMerge(clsx('flex grow flex-col gap-2 overflow-hidden', clazz))}>
103
+ <div class={merge('flex grow flex-col gap-2 overflow-hidden', clazz)}>
105
104
  <div
106
105
  class="flex grow flex-col gap-4 overflow-auto pr-2 [scrollbar-gutter:stable]"
107
106
  bind:this={chatContainer}
@@ -5,11 +5,10 @@
5
5
  -->
6
6
 
7
7
  <script lang="ts">
8
- import clsx from 'clsx';
8
+ import { merge } from '$lib/utils/merge';
9
9
  import DomPurify from 'dompurify';
10
10
  import { marked } from 'marked';
11
11
  import type { ClassValue } from 'svelte/elements';
12
- import { twMerge } from 'tailwind-merge';
13
12
 
14
13
  interface Props {
15
14
  source: string;
@@ -44,11 +43,9 @@
44
43
  </script>
45
44
 
46
45
  <div
47
- class={twMerge(
48
- clsx(
49
- 'text-surface-950-50 prose prose-strong:text-surface-950-50 prose-p:my-1 flex flex-col items-start gap-1',
50
- clazz
51
- )
46
+ class={merge(
47
+ 'text-surface-950-50 prose prose-strong:text-surface-950-50 prose-p:my-1 flex flex-col items-start gap-1',
48
+ clazz
52
49
  )}
53
50
  >
54
51
  {#await html then html}
@@ -1,8 +1,7 @@
1
1
  <script lang="ts">
2
- import clsx from 'clsx';
2
+ import { merge } from '$lib/utils/merge';
3
3
  import type { Snippet } from 'svelte';
4
4
  import type { ClassValue } from 'svelte/elements';
5
- import { twMerge } from 'tailwind-merge';
6
5
  import AttachedFile from './AttachedFile.svelte';
7
6
  import type { AiChatMessage } from './Chat.svelte';
8
7
  import Markdown from './Markdown.svelte';
@@ -24,7 +23,7 @@
24
23
  }: Props = $props();
25
24
  </script>
26
25
 
27
- <div class={twMerge(clsx('flex w-full flex-col items-end gap-1', clazz))}>
26
+ <div class={merge('flex w-full flex-col items-end gap-1', clazz)}>
28
27
  {@render messageText({ message })}
29
28
  {#if message.files}
30
29
  <div class="flex flex-row items-center gap-2">
@@ -1,10 +1,11 @@
1
1
  <script lang="ts" module>
2
+ import { theme } from '$lib/theme.svelte';
2
3
  import type { IvoryComponent } from '$lib/types';
4
+ import { merge } from '$lib/utils/merge';
3
5
  import { Check, type Icon as LucideIcon, Minus } from '@lucide/svelte';
4
6
  import clsx from 'clsx';
5
7
  import type { ClassValue } from 'svelte/elements';
6
8
  import { scale } from 'svelte/transition';
7
- import { twMerge } from 'tailwind-merge';
8
9
 
9
10
  export interface CheckboxProps extends IvoryComponent<HTMLElement> {
10
11
  class?: ClassValue;
@@ -40,16 +41,19 @@
40
41
  innerClass,
41
42
  style
42
43
  }: { icon?: typeof LucideIcon; innerClass?: string; style?: string } = $derived.by(() => {
43
- if (!checked && !partial) return { innerClass: 'border-surface-500' };
44
+ const overwrittenClass =
45
+ theme.current.checkbox?.class &&
46
+ clsx(theme.current.checkbox?.class?.(!!checked, !!partial));
47
+ if (!checked && !partial) return { innerClass: overwrittenClass ?? 'border-surface-500' };
44
48
  if (checked)
45
49
  return {
46
50
  icon: Check,
47
- innerClass: 'bg-primary-500 border-primary-500 text-surface-50'
51
+ innerClass: overwrittenClass ?? 'bg-primary-500 border-primary-500 text-surface-50'
48
52
  };
49
53
  if (partial)
50
54
  return {
51
55
  icon: Minus,
52
- innerClass: 'border-primary-700 text-primary-500'
56
+ innerClass: overwrittenClass ?? 'border-primary-700 text-primary-500'
53
57
  };
54
58
  return {};
55
59
  });
@@ -62,13 +66,11 @@
62
66
  {disabled}
63
67
  {style}
64
68
  {onclick}
65
- class={twMerge(
66
- clsx(
67
- 'box-border flex h-5 w-5 items-center justify-center overflow-hidden rounded border-2 transition-colors',
68
- disabled && 'cursor-not-allowed opacity-70',
69
- innerClass,
70
- clazz
71
- )
69
+ class={merge(
70
+ 'box-border flex h-5 w-5 items-center justify-center overflow-hidden rounded border-2 transition-colors',
71
+ disabled && 'cursor-not-allowed opacity-70',
72
+ innerClass,
73
+ clazz
72
74
  )}
73
75
  {...rest}
74
76
  >
@@ -1,9 +1,8 @@
1
1
  <script lang="ts" module>
2
2
  import type { IvoryComponent } from '$lib/types';
3
- import clsx from 'clsx';
3
+ import { merge } from '$lib/utils/merge';
4
4
  import type { Snippet } from 'svelte';
5
5
  import type { ClassValue } from 'svelte/elements';
6
- import { twMerge } from 'tailwind-merge';
7
6
 
8
7
  export interface ToggleProps extends IvoryComponent<HTMLElement> {
9
8
  value?: boolean;
@@ -18,12 +17,10 @@
18
17
 
19
18
  <svelte:element
20
19
  this={rest.onclick ? 'button' : 'div'}
21
- class={twMerge(
22
- clsx(
23
- 'group relative flex h-5 w-9 flex-row items-center rounded-full p-0.5 transition-colors duration-100',
24
- value ? 'bg-primary-500' : 'bg-surface-300-700',
25
- clazz
26
- )
20
+ class={merge(
21
+ 'group relative flex h-5 w-9 flex-row items-center rounded-full p-0.5 transition-colors duration-100',
22
+ value ? 'bg-primary-500' : 'bg-surface-300-700',
23
+ clazz
27
24
  )}
28
25
  type={rest.onclick ? 'button' : undefined}
29
26
  role={rest.onclick ? 'button' : undefined}
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ import type { ClassValue } from 'svelte/elements';
3
+ import { Checkbox } from '../basic';
4
+ import type { InputProps } from './Input.svelte';
5
+ import FormIssues from './issues/FormIssues.svelte';
6
+
7
+ interface Props extends InputProps<boolean> {
8
+ class?: ClassValue;
9
+ label: string;
10
+ description?: string;
11
+ onclick?: () => void;
12
+ }
13
+
14
+ let { class: clazz = '', label, description, onclick, disabled, form }: Props = $props();
15
+
16
+ const { set, issues, value } = $derived(form);
17
+
18
+ const checked = $derived(value());
19
+ </script>
20
+
21
+ <div class={['flex flex-col', clazz]}>
22
+ <button
23
+ type="button"
24
+ {disabled}
25
+ onclick={onclick ||
26
+ (() => {
27
+ set(!checked);
28
+ })}
29
+ class={['flex flex-row items-center gap-2', disabled && 'opacity-80']}
30
+ >
31
+ <Checkbox {checked} />
32
+ {label}
33
+ </button>
34
+ <input class="hidden" {...form.as('checkbox')} {checked} />
35
+ {#if description}
36
+ <p class="text-surface-700-300">{description}</p>
37
+ {/if}
38
+ <FormIssues issues={issues?.()} />
39
+ </div>
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ import { merge } from '$lib/utils/merge';
3
+ import Input, { INPUT_UNSET_OUTLINE, type InputProps } from './Input.svelte';
4
+
5
+ let props: InputProps<string> = $props();
6
+ </script>
7
+
8
+ <Input {...props}>
9
+ {#snippet children({ id, class: inputClass })}
10
+ <div
11
+ class={merge([inputClass, 'flex grow flex-row items-center justify-between gap-4 p-0'])}
12
+ >
13
+ <input
14
+ type="text"
15
+ bind:value={props.form.value, props.form.set}
16
+ class={['flex h-full grow items-center bg-transparent pl-2', INPUT_UNSET_OUTLINE]}
17
+ />
18
+ <input
19
+ class={['h-full rounded', INPUT_UNSET_OUTLINE]}
20
+ {id}
21
+ {...props.form.as('color')}
22
+ />
23
+ </div>
24
+ {/snippet}
25
+ </Input>
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ import Input, { type InputProps } from './Input.svelte';
3
+
4
+ let props: InputProps<string> = $props();
5
+ </script>
6
+
7
+ <Input {...props}>
8
+ {#snippet children(inputProps)}
9
+ <input {...inputProps} {...props.form.as?.('date')} />
10
+ {/snippet}
11
+ </Input>
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import Input, { type InputProps } from './Input.svelte';
3
+
4
+ let props: InputProps<string> = $props();
5
+ </script>
6
+
7
+ <Input {...props}>
8
+ {#snippet children(inputProps)}
9
+ <input
10
+ {...inputProps}
11
+ {...props.form.as?.('email')}
12
+ autocomplete="email"
13
+ autocapitalize="off"
14
+ />
15
+ {/snippet}
16
+ </Input>
@@ -0,0 +1,131 @@
1
+ <script lang="ts" module>
2
+ import { FileUp, X } from '@lucide/svelte';
3
+ import type { Snippet } from 'svelte';
4
+ import type { InputProps } from './Input.svelte';
5
+ import Input from './Input.svelte';
6
+
7
+ export interface FileInputProps extends InputProps<File[] | File> {
8
+ accept?: string[];
9
+ children?: Snippet;
10
+ multiple?: boolean;
11
+ }
12
+ </script>
13
+
14
+ <script lang="ts">
15
+ let { accept, children: passedChildren, multiple = false, ...rest }: FileInputProps = $props();
16
+
17
+ let hovering = $state(false);
18
+
19
+ function onDrop(
20
+ event: DragEvent & {
21
+ currentTarget: EventTarget & HTMLElement;
22
+ }
23
+ ) {
24
+ add(event.dataTransfer?.files ? Array.from(event.dataTransfer.files) : []);
25
+ }
26
+
27
+ function add(files: File[]) {
28
+ const value = rest.form.value();
29
+ if (Array.isArray(value)) {
30
+ rest.form.set([...value.filter((f) => !files.includes(f)), ...Array.from(files || [])]);
31
+ } else {
32
+ rest.form.set((files[0] ?? undefined) as File);
33
+ }
34
+ }
35
+
36
+ function remove(file: File) {
37
+ const value = rest.form.value();
38
+ if (Array.isArray(value)) {
39
+ rest.form.set(value.filter((e) => e !== file));
40
+ } else {
41
+ rest.form.set(undefined as unknown as File);
42
+ }
43
+ }
44
+
45
+ const files = $derived.by(() => {
46
+ const value = rest.form.value();
47
+ if (Array.isArray(value)) {
48
+ return value;
49
+ } else {
50
+ return value ? [value] : [];
51
+ }
52
+ });
53
+
54
+ const formAttributes = $derived(rest.form.as(multiple ? 'file multiple' : 'file'));
55
+ </script>
56
+
57
+ <Input {...rest} fixTitle>
58
+ {#snippet children({ id })}
59
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
60
+ <div
61
+ class={[
62
+ 'group relative h-full min-h-16 w-full rounded transition-all',
63
+ hovering ? 'bg-primary-50-950' : ''
64
+ ]}
65
+ ondragover={() => {
66
+ hovering = true;
67
+ }}
68
+ ondragleave={() => {
69
+ hovering = false;
70
+ }}
71
+ ondrop={(e) => {
72
+ e.preventDefault();
73
+ e.stopPropagation();
74
+ hovering = false;
75
+ onDrop(e);
76
+ }}
77
+ >
78
+ {#if passedChildren}
79
+ {@render passedChildren()}
80
+ {:else}
81
+ <div class="flex h-full w-full flex-col items-center justify-center gap-2 p-4">
82
+ {#if files && files.length > 0}
83
+ <div class="flex flex-row items-center gap-4">
84
+ {#each files as file (file)}
85
+ {@render fileRender(file)}
86
+ {/each}
87
+ </div>
88
+ {:else}
89
+ <div
90
+ class="text-primary-200-800 group-hover:text-primary-300-700 group-hover:bg-primary-100-900 flex h-14 w-14 shrink-0 items-center justify-center rounded-full text-xl transition-colors"
91
+ >
92
+ <FileUp />
93
+ </div>
94
+ {/if}
95
+ </div>
96
+ {/if}
97
+ <input
98
+ type={formAttributes.type}
99
+ aria-invalid={formAttributes['aria-invalid']}
100
+ name={formAttributes.name}
101
+ oninput={(e) => {
102
+ e.preventDefault();
103
+ e.stopPropagation();
104
+ add(e.currentTarget.files ? Array.from(e.currentTarget.files) : []);
105
+ }}
106
+ {multiple}
107
+ title=""
108
+ accept={accept?.join(', ')}
109
+ class="absolute top-0 left-0 h-full w-full grow cursor-pointer opacity-0"
110
+ {id}
111
+ />
112
+ </div>
113
+ {/snippet}
114
+ </Input>
115
+
116
+ {#snippet fileRender(file: File)}
117
+ <div class="bg-primary-200-800 flex w-fit items-center rounded-full py-2 pr-2 pl-4">
118
+ <p>{file.name}</p>
119
+ <button
120
+ type="button"
121
+ class="btn-icon hover:text-primary-500"
122
+ onclick={(e) => {
123
+ e.preventDefault();
124
+ e.stopPropagation();
125
+ remove(file);
126
+ }}
127
+ >
128
+ <X />
129
+ </button>
130
+ </div>
131
+ {/snippet}