@tableslayer/ui 0.1.3 → 0.1.4

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 (205) hide show
  1. package/package.json +2 -13
  2. package/src/lib/components/Avatar/Avatar.svelte +82 -0
  3. package/src/lib/components/Avatar/AvatarFileInput.svelte +85 -0
  4. package/src/lib/components/Avatar/AvatarPopover.svelte +34 -0
  5. package/src/lib/components/Avatar/index.ts +4 -0
  6. package/src/lib/components/Avatar/types.ts +24 -0
  7. package/src/lib/components/BrushSizeSlider/BrushSizeSlider.svelte +174 -0
  8. package/src/lib/components/BrushSizeSlider/index.ts +1 -0
  9. package/src/lib/components/Button/Button.svelte +182 -0
  10. package/src/lib/components/Button/ConfirmActionButton.svelte +98 -0
  11. package/src/lib/components/Button/IconButton.svelte +121 -0
  12. package/src/lib/components/Button/RadioButton.svelte +93 -0
  13. package/src/lib/components/Button/index.ts +5 -0
  14. package/src/lib/components/Button/types.ts +54 -0
  15. package/src/lib/components/CardFan/CardFan.svelte +165 -0
  16. package/src/lib/components/CardFan/index.ts +2 -0
  17. package/src/lib/components/CardFan/types.ts +6 -0
  18. package/src/lib/components/CodeBlock/Code.svelte +7 -0
  19. package/src/lib/components/CodeBlock/CodeBlock.svelte +102 -0
  20. package/src/lib/components/CodeBlock/index.ts +3 -0
  21. package/src/lib/components/CodeBlock/types.ts +10 -0
  22. package/src/lib/components/ColorMode/ColorMode.svelte +8 -0
  23. package/src/lib/components/ColorMode/index.ts +2 -0
  24. package/src/lib/components/ColorMode/types.ts +12 -0
  25. package/src/lib/components/ColorPicker/ColorPicker.svelte +838 -0
  26. package/src/lib/components/ColorPicker/ColorPickerSwatch.svelte +32 -0
  27. package/src/lib/components/ColorPicker/index.ts +3 -0
  28. package/src/lib/components/ColorPicker/types.ts +51 -0
  29. package/src/lib/components/ContextMenu/ContextMenu.svelte +86 -0
  30. package/src/lib/components/ContextMenu/index.ts +2 -0
  31. package/src/lib/components/ContextMenu/types.ts +15 -0
  32. package/src/lib/components/DrawingSliders/DrawingSliders.svelte +379 -0
  33. package/src/lib/components/DrawingSliders/index.ts +1 -0
  34. package/src/lib/components/Editor/Editor.svelte +825 -0
  35. package/src/lib/components/Editor/index.ts +1 -0
  36. package/src/lib/components/FogSliders/FogSliders.svelte +33 -0
  37. package/src/lib/components/FogSliders/index.ts +1 -0
  38. package/src/lib/components/Hr/Hr.svelte +15 -0
  39. package/src/lib/components/Hr/index.ts +1 -0
  40. package/src/lib/components/Icon/Icon.svelte +6 -0
  41. package/src/lib/components/Icon/index.ts +2 -0
  42. package/src/lib/components/Icon/types.ts +20 -0
  43. package/src/lib/components/Input/DualInputSlider.svelte +126 -0
  44. package/src/lib/components/Input/FileInput.svelte +176 -0
  45. package/src/lib/components/Input/FormControl.svelte +150 -0
  46. package/src/lib/components/Input/FormError.svelte +37 -0
  47. package/src/lib/components/Input/Input.svelte +56 -0
  48. package/src/lib/components/Input/InputCheckbox.svelte +99 -0
  49. package/src/lib/components/Input/InputSlider.svelte +86 -0
  50. package/src/lib/components/Input/Label.svelte +19 -0
  51. package/src/lib/components/Input/index.ts +9 -0
  52. package/src/lib/components/Input/types.ts +39 -0
  53. package/src/lib/components/Link/Link.svelte +41 -0
  54. package/src/lib/components/Link/LinkBox.svelte +20 -0
  55. package/src/lib/components/Link/LinkOverlay.svelte +23 -0
  56. package/src/lib/components/Link/index.ts +4 -0
  57. package/src/lib/components/Link/types.ts +17 -0
  58. package/src/lib/components/Loading/Loader.svelte +60 -0
  59. package/src/lib/components/Loading/Skeleton.svelte +9 -0
  60. package/src/lib/components/Loading/index.ts +2 -0
  61. package/src/lib/components/Logo/Logo.svelte +16 -0
  62. package/src/lib/components/Logo/index.ts +1 -0
  63. package/src/lib/components/MarkerTooltip/MarkerTooltip.svelte +435 -0
  64. package/src/lib/components/MarkerTooltip/index.ts +1 -0
  65. package/src/lib/components/Menu/SelectorMenu.svelte +280 -0
  66. package/src/lib/components/Menu/index.ts +2 -0
  67. package/src/lib/components/Menu/types.ts +17 -0
  68. package/src/lib/components/MyCounterButton.svelte +11 -0
  69. package/src/lib/components/Panel/index.ts +2 -0
  70. package/src/lib/components/Panel/panel.svelte +18 -0
  71. package/src/lib/components/Panel/types.ts +8 -0
  72. package/src/lib/components/PersistButton/PersistButton.svelte +100 -0
  73. package/src/lib/components/PersistButton/index.ts +1 -0
  74. package/src/lib/components/Popover/Popover.svelte +81 -0
  75. package/src/lib/components/Popover/index.ts +2 -0
  76. package/src/lib/components/Popover/types.ts +19 -0
  77. package/src/lib/components/PropsTable/PropsTable.svelte +107 -0
  78. package/src/lib/components/RadialMenu/EffectPreview.svelte +36 -0
  79. package/src/lib/components/RadialMenu/EffectPreviewScene.svelte +194 -0
  80. package/src/lib/components/RadialMenu/RadialMenu.svelte +503 -0
  81. package/src/lib/components/RadialMenu/RadialMenuItem.svelte +176 -0
  82. package/src/lib/components/RadialMenu/index.ts +2 -0
  83. package/src/lib/components/RadialMenu/types.ts +35 -0
  84. package/src/lib/components/Select/Select.svelte +342 -0
  85. package/src/lib/components/Select/index.ts +2 -0
  86. package/src/lib/components/Select/types.ts +22 -0
  87. package/src/lib/components/Spacer/Spacer.svelte +14 -0
  88. package/src/lib/components/Spacer/index.ts +2 -0
  89. package/src/lib/components/Spacer/types.ts +5 -0
  90. package/src/lib/components/Stage/components/AnnotationLayer/AnnotationLayer.svelte +445 -0
  91. package/src/lib/components/Stage/components/AnnotationLayer/AnnotationMaterial.svelte +167 -0
  92. package/src/lib/components/Stage/components/AnnotationLayer/types.ts +196 -0
  93. package/src/lib/components/Stage/components/CursorLayer/CursorLayer.svelte +148 -0
  94. package/src/lib/components/Stage/components/CursorLayer/cursor.svg +26 -0
  95. package/src/lib/components/Stage/components/CursorLayer/index.ts +2 -0
  96. package/src/lib/components/Stage/components/CursorLayer/types.ts +23 -0
  97. package/src/lib/components/Stage/components/DrawingLayer/DrawingMaterial.svelte +364 -0
  98. package/src/lib/components/Stage/components/DrawingLayer/types.ts +65 -0
  99. package/src/lib/components/Stage/components/EdgeOverlayLayer/EdgeOverlayLayer.svelte +72 -0
  100. package/src/lib/components/Stage/components/EdgeOverlayLayer/types.ts +34 -0
  101. package/src/lib/components/Stage/components/FogLayer/FogLayer.svelte +75 -0
  102. package/src/lib/components/Stage/components/FogLayer/types.ts +51 -0
  103. package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarLayer.svelte +249 -0
  104. package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarMaterial.svelte +200 -0
  105. package/src/lib/components/Stage/components/FogOfWarLayer/types.ts +116 -0
  106. package/src/lib/components/Stage/components/GridLayer/GridLayer.svelte +20 -0
  107. package/src/lib/components/Stage/components/GridLayer/GridMaterial.svelte +69 -0
  108. package/src/lib/components/Stage/components/GridLayer/types.ts +79 -0
  109. package/src/lib/components/Stage/components/LayerInput/LayerInput.svelte +300 -0
  110. package/src/lib/components/Stage/components/MapLayer/MapLayer.svelte +196 -0
  111. package/src/lib/components/Stage/components/MapLayer/dataSources/GifDataSource.ts +265 -0
  112. package/src/lib/components/Stage/components/MapLayer/dataSources/IMapDataSource.ts +55 -0
  113. package/src/lib/components/Stage/components/MapLayer/dataSources/ImageDataSource.ts +87 -0
  114. package/src/lib/components/Stage/components/MapLayer/dataSources/VideoDataSource.ts +150 -0
  115. package/src/lib/components/Stage/components/MapLayer/dataSources/dataSourceFactory.ts +48 -0
  116. package/src/lib/components/Stage/components/MapLayer/dataSources/index.ts +16 -0
  117. package/src/lib/components/Stage/components/MapLayer/types.ts +58 -0
  118. package/src/lib/components/Stage/components/MarkerLayer/MarkerLayer.svelte +398 -0
  119. package/src/lib/components/Stage/components/MarkerLayer/MarkerToken.svelte +262 -0
  120. package/src/lib/components/Stage/components/MarkerLayer/types.ts +126 -0
  121. package/src/lib/components/Stage/components/MeasurementLayer/MeasurementLayer.svelte +364 -0
  122. package/src/lib/components/Stage/components/MeasurementLayer/MeasurementManager.svelte +473 -0
  123. package/src/lib/components/Stage/components/MeasurementLayer/measurements/BaseMeasurement.ts +427 -0
  124. package/src/lib/components/Stage/components/MeasurementLayer/measurements/BeamMeasurement.ts +105 -0
  125. package/src/lib/components/Stage/components/MeasurementLayer/measurements/CircleMeasurement.ts +98 -0
  126. package/src/lib/components/Stage/components/MeasurementLayer/measurements/ConeMeasurement.ts +163 -0
  127. package/src/lib/components/Stage/components/MeasurementLayer/measurements/LineMeasurement.ts +102 -0
  128. package/src/lib/components/Stage/components/MeasurementLayer/measurements/RectangleMeasurement.ts +120 -0
  129. package/src/lib/components/Stage/components/MeasurementLayer/measurements/index.ts +7 -0
  130. package/src/lib/components/Stage/components/MeasurementLayer/types.ts +94 -0
  131. package/src/lib/components/Stage/components/MeasurementLayer/utils/canvasDrawing.ts +357 -0
  132. package/src/lib/components/Stage/components/MeasurementLayer/utils/distanceCalculations.ts +170 -0
  133. package/src/lib/components/Stage/components/ParticleSystem/ParticleSystem.svelte +220 -0
  134. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/ash.png +0 -0
  135. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/leaves.png +0 -0
  136. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/rain.png +0 -0
  137. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/snow.png +0 -0
  138. package/src/lib/components/Stage/components/ParticleSystem/rng.js +20 -0
  139. package/src/lib/components/Stage/components/ParticleSystem/types.ts +95 -0
  140. package/src/lib/components/Stage/components/PerformanceDebugger/PerformanceDebugger.svelte +144 -0
  141. package/src/lib/components/Stage/components/PerformanceDebugger/index.ts +1 -0
  142. package/src/lib/components/Stage/components/PerformanceOverlay/PerformanceOverlay.svelte +208 -0
  143. package/src/lib/components/Stage/components/PerformanceOverlay/index.ts +1 -0
  144. package/src/lib/components/Stage/components/PointerInputManager/PointerInputManager.svelte +201 -0
  145. package/src/lib/components/Stage/components/Scene/Scene.svelte +651 -0
  146. package/src/lib/components/Stage/components/Scene/luts.ts +24 -0
  147. package/src/lib/components/Stage/components/Scene/types.ts +225 -0
  148. package/src/lib/components/Stage/components/Stage/Stage.svelte +332 -0
  149. package/src/lib/components/Stage/components/Stage/types.ts +136 -0
  150. package/src/lib/components/Stage/components/WeatherLayer/WeatherLayer.svelte +135 -0
  151. package/src/lib/components/Stage/components/WeatherLayer/presets/AshPreset.ts +71 -0
  152. package/src/lib/components/Stage/components/WeatherLayer/presets/LeavesPreset.ts +70 -0
  153. package/src/lib/components/Stage/components/WeatherLayer/presets/RainPreset.ts +68 -0
  154. package/src/lib/components/Stage/components/WeatherLayer/presets/SnowPreset.ts +70 -0
  155. package/src/lib/components/Stage/components/WeatherLayer/presets/index.ts +6 -0
  156. package/src/lib/components/Stage/components/WeatherLayer/types.ts +35 -0
  157. package/src/lib/components/Stage/helpers/clippingPlaneStore.svelte.ts +28 -0
  158. package/src/lib/components/Stage/helpers/debugState.svelte.ts +18 -0
  159. package/src/lib/components/Stage/helpers/grid.ts +548 -0
  160. package/src/lib/components/Stage/helpers/lazyBrush.ts +171 -0
  161. package/src/lib/components/Stage/helpers/performanceMetrics.svelte.ts +220 -0
  162. package/src/lib/components/Stage/helpers/utils.ts +21 -0
  163. package/src/lib/components/Stage/index.ts +49 -0
  164. package/src/lib/components/Stage/shaders/AnnotationEffects.frag +1070 -0
  165. package/src/lib/components/Stage/shaders/Annotations.frag +29 -0
  166. package/src/lib/components/Stage/shaders/Drawing.frag +83 -0
  167. package/src/lib/components/Stage/shaders/Drawing.vert +5 -0
  168. package/src/lib/components/Stage/shaders/Fog.frag +147 -0
  169. package/src/lib/components/Stage/shaders/FractalNoise.frag +96 -0
  170. package/src/lib/components/Stage/shaders/GridShader.frag +174 -0
  171. package/src/lib/components/Stage/shaders/Overlay.frag +23 -0
  172. package/src/lib/components/Stage/shaders/Overlay.vert +0 -0
  173. package/src/lib/components/Stage/shaders/Particles.frag +27 -0
  174. package/src/lib/components/Stage/shaders/Particles.vert +51 -0
  175. package/src/lib/components/Stage/shaders/ToolOutline.frag +59 -0
  176. package/src/lib/components/Stage/shaders/default.vert +8 -0
  177. package/src/lib/components/Stage/types.ts +4 -0
  178. package/src/lib/components/Table/Table.svelte +16 -0
  179. package/src/lib/components/Table/Td.svelte +17 -0
  180. package/src/lib/components/Table/Th.svelte +18 -0
  181. package/src/lib/components/Table/index.ts +4 -0
  182. package/src/lib/components/Table/types.ts +14 -0
  183. package/src/lib/components/Text/Text.svelte +23 -0
  184. package/src/lib/components/Text/index.ts +2 -0
  185. package/src/lib/components/Text/types.ts +12 -0
  186. package/src/lib/components/Title/Title.svelte +54 -0
  187. package/src/lib/components/Title/index.ts +2 -0
  188. package/src/lib/components/Title/types.ts +9 -0
  189. package/src/lib/components/Toast/Toast.svelte +155 -0
  190. package/src/lib/components/Toast/index.ts +5 -0
  191. package/src/lib/components/Toast/toastCookie.ts +24 -0
  192. package/src/lib/components/Toast/types.ts +6 -0
  193. package/src/lib/components/ToolTip/ToolTip.svelte +70 -0
  194. package/src/lib/components/ToolTip/index.ts +2 -0
  195. package/src/lib/components/ToolTip/types.ts +14 -0
  196. package/src/lib/components/index.ts +32 -0
  197. package/src/lib/components/types.ts +0 -0
  198. package/src/lib/index.ts +2 -0
  199. package/src/lib/styles/globals.css +108 -0
  200. package/src/lib/styles/normalize.css +9 -0
  201. package/src/lib/styles/reset.css +133 -0
  202. package/src/lib/styles/utilities.css +179 -0
  203. package/src/lib/styles/vars.css +1103 -0
  204. package/src/lib/types/awareness.ts +17 -0
  205. package/src/lib/utils/rle.ts +217 -0
@@ -0,0 +1 @@
1
+ export { default as Editor } from './Editor.svelte';
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import { BrushSizeSlider } from '../BrushSizeSlider';
3
+
4
+ interface Props {
5
+ brushSize: number;
6
+ onBrushSizeChange: (value: number) => void;
7
+ }
8
+
9
+ let { brushSize, onBrushSizeChange }: Props = $props();
10
+ </script>
11
+
12
+ <div class="fogSliders">
13
+ <BrushSizeSlider {brushSize} {onBrushSizeChange} min={5} max={20} curve="linear" displayAsPercentage={true} />
14
+ </div>
15
+
16
+ <style>
17
+ .fogSliders {
18
+ position: absolute;
19
+ top: 50%;
20
+ right: 1rem;
21
+ transform: translateY(-50%);
22
+ display: flex;
23
+ flex-direction: column;
24
+ gap: 1rem;
25
+ z-index: 10;
26
+ pointer-events: auto;
27
+ background-color: var(--bg);
28
+ border: var(--borderThin);
29
+ border-radius: var(--radius-2);
30
+ padding: 0.5rem 0rem;
31
+ align-items: center;
32
+ }
33
+ </style>
@@ -0,0 +1 @@
1
+ export { default as FogSliders } from './FogSliders.svelte';
@@ -0,0 +1,15 @@
1
+ <script lang="ts">
2
+ let { ...restProps } = $props();
3
+
4
+ let hrClasses = $derived(['hr', restProps.class ?? '']);
5
+ </script>
6
+
7
+ <hr class={hrClasses} {...restProps} />
8
+
9
+ <style>
10
+ .hr {
11
+ height: 1px;
12
+ width: 100%;
13
+ background: var(--contrastMedium);
14
+ }
15
+ </style>
@@ -0,0 +1 @@
1
+ export { default as Hr } from './Hr.svelte';
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import type { IconProps } from './types';
3
+ let { Icon, size = '1rem', color, stroke = 2, ...restProps }: IconProps = $props();
4
+ </script>
5
+
6
+ <Icon {size} {color} {stroke} {...restProps} style={`min-width: ${size};min-height: ${size}`}></Icon>
@@ -0,0 +1,2 @@
1
+ export { default as Icon } from './Icon.svelte';
2
+ export type { IconProps } from './types';
@@ -0,0 +1,20 @@
1
+ import type { HTMLBaseAttributes } from 'svelte/elements';
2
+
3
+ export type IconProps = {
4
+ /**
5
+ * Any icon from [tabler-icons](https://tabler.io/icons)
6
+ */
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ Icon: any;
9
+ /**
10
+ * The size of the icon.
11
+ * @default 24
12
+ */
13
+ size?: number | string;
14
+ /**
15
+ * The color of the icon.
16
+ * @default 'currentColor'
17
+ * */
18
+ color?: string;
19
+ stroke?: number;
20
+ } & HTMLBaseAttributes;
@@ -0,0 +1,126 @@
1
+ <script lang="ts">
2
+ import { type DualInputSliderProps } from './types';
3
+
4
+ let {
5
+ valueStart = $bindable(),
6
+ valueEnd = $bindable(),
7
+ min,
8
+ max,
9
+ step,
10
+ color = 'var(--fg)',
11
+ ...restProps
12
+ }: DualInputSliderProps = $props();
13
+
14
+ const sliderClasses = $derived(['dualInputSlider', restProps.class ?? '']);
15
+
16
+ function handleInputStart(event: Event) {
17
+ let newValue = parseFloat((event.target as HTMLInputElement).value);
18
+ newValue = Math.round(newValue / step) * step; // Fix floating-point precision
19
+ valueStart = Math.min(newValue, valueEnd - step); // Ensure start does not exceed end
20
+ }
21
+
22
+ function handleInputEnd(event: Event) {
23
+ let newValue = parseFloat((event.target as HTMLInputElement).value);
24
+ newValue = Math.round(newValue / step) * step; // Fix floating-point precision
25
+ valueEnd = Math.max(newValue, valueStart + step); // Ensure end is greater than start
26
+ }
27
+
28
+ // ✅ Corrected track position calculations
29
+ const leftPos = $derived(() => ((valueStart - min) / (max - min)) * 100);
30
+ const rightPos = $derived(() => 100 - ((valueEnd - min) / (max - min)) * 100);
31
+ </script>
32
+
33
+ <div class="dualInputSlider__wrap">
34
+ <div class="dualInputSlider__bgTrack">
35
+ <div
36
+ class="dualInputSlider__activeTrack"
37
+ style={`left: ${leftPos()}%;
38
+ right: ${rightPos()}%;
39
+ background: ${color};`}
40
+ ></div>
41
+
42
+ <input type="range" {min} {max} {step} bind:value={valueStart} oninput={handleInputStart} class={sliderClasses} />
43
+ <input type="range" {min} {max} {step} bind:value={valueEnd} oninput={handleInputEnd} class={sliderClasses} />
44
+ </div>
45
+ </div>
46
+
47
+ <style>
48
+ .dualInputSlider__wrap {
49
+ display: flex;
50
+ width: 100%;
51
+ align-items: center;
52
+ height: 2rem;
53
+ }
54
+ .dualInputSlider__bgTrack {
55
+ position: relative;
56
+ width: 100%;
57
+ height: 4px;
58
+ display: flex;
59
+ align-items: center;
60
+ background: var(--inputBorderColor);
61
+ }
62
+
63
+ .dualInputSlider {
64
+ -webkit-appearance: none;
65
+ appearance: none;
66
+ position: absolute;
67
+ appearance: none;
68
+ position: absolute;
69
+ width: 100%;
70
+ height: 3px;
71
+ pointer-events: none;
72
+ top: 50%;
73
+ transform: translateY(-50%);
74
+ z-index: 3;
75
+ }
76
+
77
+ .dualInputSlider::-webkit-slider-thumb {
78
+ width: 0.5rem;
79
+ height: 0.5rem;
80
+ border-radius: 50%;
81
+ border: 2px solid white;
82
+ background-color: var(--inputBorderColor);
83
+ box-shadow: 0 0 2px rgba(0, 0, 0, 1);
84
+ pointer-events: all;
85
+ cursor: pointer;
86
+ }
87
+ .dualInputSlider::-moz-range-thumb {
88
+ width: 0.5rem;
89
+ height: 0.5rem;
90
+ border-radius: 50%;
91
+ border: 2px solid white;
92
+ background-color: var(--inputBorderColor);
93
+ box-shadow: 0 0 2px rgba(0, 0, 0, 1);
94
+ pointer-events: all;
95
+ cursor: pointer;
96
+ }
97
+
98
+ .dualInputSlider__activeTrack {
99
+ position: absolute;
100
+ z-index: 1;
101
+ height: 3px;
102
+ top: 50%;
103
+ transform: translateY(-50%);
104
+ pointer-events: none;
105
+ z-index: 2;
106
+ }
107
+
108
+ @media (pointer: coarse) {
109
+ .dualInputSlider__activeTrack {
110
+ height: 1rem;
111
+ border-radius: var(--radius-2);
112
+ }
113
+ .dualInputSlider__bgTrack {
114
+ height: 1rem;
115
+ border-radius: var(--radius-2);
116
+ }
117
+ .dualInputSlider::-webkit-slider-thumb {
118
+ width: 1rem;
119
+ height: 1rem;
120
+ }
121
+ .dualInputSlider::-moz-range-thumb {
122
+ width: 1rem;
123
+ height: 1rem;
124
+ }
125
+ }
126
+ </style>
@@ -0,0 +1,176 @@
1
+ <script lang="ts">
2
+ import type { FileInputProps } from './types';
3
+ import { onDestroy, createEventDispatcher } from 'svelte';
4
+
5
+ const dispatch = createEventDispatcher<{
6
+ fileSelect: File;
7
+ }>();
8
+
9
+ let {
10
+ value = $bindable(),
11
+ files = $bindable(),
12
+ variant = 'default',
13
+ accept = 'image/png, image/jpeg, image/webp, image/gif',
14
+ showPreviews = true,
15
+ ...restProps
16
+ }: FileInputProps = $props();
17
+
18
+ let inputClasses = $derived(['fileInput', variant && `fileInput--${variant}`, restProps.class ?? '']);
19
+
20
+ // Default label text based on accept prop
21
+ let labelText = $derived(() => {
22
+ if (accept.includes('image/')) {
23
+ return 'Select an image';
24
+ } else if (accept.includes('.json')) {
25
+ return 'Select a file';
26
+ } else {
27
+ return 'Select a file';
28
+ }
29
+ });
30
+
31
+ // Array to keep track of generated object URLs for cleanup
32
+ let generatedUrls: string[] = [];
33
+
34
+ // Function to clean up generated URLs
35
+ const previousUrlsCleanup = () => {
36
+ for (const url of generatedUrls) {
37
+ URL.revokeObjectURL(url);
38
+ }
39
+ generatedUrls = [];
40
+ };
41
+
42
+ // Function to check if file is an image
43
+ const isImageFile = (file: File): boolean => {
44
+ return file.type.startsWith('image/');
45
+ };
46
+
47
+ // Create a derived value for preview URLs based on `files`
48
+ let previewUrls = $derived.by(() => {
49
+ if (files && files.length > 0 && showPreviews) {
50
+ const urls = [];
51
+ for (const file of files) {
52
+ // Only create previews for image files
53
+ if (file && isImageFile(file)) {
54
+ const objectUrl = URL.createObjectURL(file);
55
+ urls.push(objectUrl);
56
+ generatedUrls.push(objectUrl); // Keep track of generated URLs for cleanup
57
+ }
58
+ }
59
+ return urls;
60
+ }
61
+ return [];
62
+ });
63
+
64
+ // Handle file selection
65
+ function handleFileChange() {
66
+ if (files && files.length > 0) {
67
+ // Dispatch the first file
68
+ dispatch('fileSelect', files[0]);
69
+ }
70
+ }
71
+
72
+ // Clean up the object URLs to prevent memory leaks when the component is destroyed
73
+ onDestroy(() => {
74
+ previousUrlsCleanup();
75
+ });
76
+ </script>
77
+
78
+ <div class={inputClasses}>
79
+ {#if variant === 'dropzone'}
80
+ <label class="fileInput__dropZoneLabel" for={restProps.id}><div>{labelText}</div></label>
81
+ {/if}
82
+ <!-- File input with configurable accept attribute -->
83
+ <input bind:files type="file" class="fileInput__input" {accept} onchange={handleFileChange} {...restProps} />
84
+
85
+ <!-- Display previews for image files if showPreviews is true -->
86
+ {#if showPreviews}
87
+ {#each previewUrls as previewUrl}
88
+ <img src={previewUrl} alt="Preview" class="fileInput__preview" />
89
+ {/each}
90
+ {/if}
91
+ </div>
92
+
93
+ <style>
94
+ .fileInput {
95
+ display: flex;
96
+ flex-direction: column;
97
+ align-items: center;
98
+ justify-content: center;
99
+ }
100
+ .fileInput__input::file-selector-button {
101
+ background: var(--inputBg);
102
+ padding: 0 var(--size-2);
103
+ color: var(--fg);
104
+ border: var(--borderThin);
105
+ border-radius: var(--radius-2);
106
+ padding: 0.25rem 0.75rem;
107
+ font-weight: 600;
108
+ cursor: pointer;
109
+ display: inline-flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ }
113
+ .fileInput__input::file-selector-button:hover {
114
+ background: var(--inputFocusBg);
115
+ }
116
+ [data-fs-error] {
117
+ border-color: var(--fgDanger);
118
+ }
119
+ .fileInput__preview {
120
+ max-width: 8rem;
121
+ max-height: 8rem;
122
+ margin-top: 1rem;
123
+ margin-right: 0.5rem;
124
+ }
125
+ .fileInput--dropzone {
126
+ width: 100%;
127
+ max-width: 100%;
128
+ cursor: pointer;
129
+ position: relative;
130
+ aspect-ratio: 16 / 9;
131
+ overflow: hidden;
132
+ }
133
+
134
+ .fileInput--dropzone .fileInput__preview {
135
+ margin: 0;
136
+ max-width: 100%;
137
+ max-height: 100%;
138
+ object-fit: cover;
139
+ }
140
+ .fileInput--dropzone input {
141
+ visibility: hidden;
142
+ display: none;
143
+ }
144
+ .fileInput--dropzone .fileInput__dropZoneLabel {
145
+ position: absolute;
146
+ display: flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ width: 100%;
150
+ height: 100%;
151
+ z-index: 2;
152
+ top: 0;
153
+ left: 0;
154
+ }
155
+ .fileInput--dropzone .fileInput__dropZoneLabel div {
156
+ background: rgba(0, 0, 0, 0.8);
157
+ color: white;
158
+ padding: 0.25rem 0.5rem;
159
+ border-radius: var(--radius-1);
160
+ font-size: 0.85rem;
161
+ }
162
+ .fileInput--transparent {
163
+ .fileInput__input {
164
+ opacity: 0;
165
+ border: none;
166
+ position: absolute;
167
+ top: 0;
168
+ left: 0;
169
+ width: 100%;
170
+ height: 100%;
171
+ }
172
+ .fileInput__preview {
173
+ display: none;
174
+ }
175
+ }
176
+ </style>
@@ -0,0 +1,150 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { Label } from './';
4
+ import type { HTMLBaseAttributes } from 'svelte/elements';
5
+ import { IconX } from '@tabler/icons-svelte';
6
+ import { Icon } from '../';
7
+ import { type $ZodIssue } from 'zod/v4/core';
8
+ import { v4 as uuidv4 } from 'uuid';
9
+ type Props = {
10
+ /**
11
+ * The input component that will be rendered
12
+ */
13
+ input: Snippet<
14
+ [
15
+ {
16
+ inputProps: {
17
+ id: string;
18
+ name: string;
19
+ 'aria-describedby'?: string;
20
+ 'aria-invalid'?: boolean;
21
+ };
22
+ }
23
+ ]
24
+ >;
25
+ /**
26
+ * The label for the input
27
+ */
28
+ label?: string;
29
+ /**
30
+ * A snippet that will be rendered at the start of the input
31
+ */
32
+ start?: Snippet;
33
+ /**
34
+ * A snippet that will be rendered at the end of the input
35
+ */
36
+ end?: Snippet;
37
+ /**
38
+ * The name needs to match a "path" in our zod schema
39
+ */
40
+ name: string;
41
+ /**
42
+ * This shape matches what returns from zod / mutation factories
43
+ */
44
+ errors?: $ZodIssue[];
45
+ } & HTMLBaseAttributes;
46
+
47
+ let { input, label, start, name, errors, end, ...restProps }: Props = $props();
48
+ let error = $derived(errors?.find((error) => error.path.includes(name)));
49
+
50
+ const id = `control-${uuidv4()}`;
51
+
52
+ let inputProps = $derived.by(() => {
53
+ return {
54
+ id,
55
+ name,
56
+ 'aria-describedby': error && `error-${id}`,
57
+ 'aria-invalid': !!error
58
+ };
59
+ });
60
+
61
+ let inputWrapperClasses = $derived([
62
+ 'control__inputWrapper',
63
+ start && 'control__inputWrapper--start',
64
+ end && 'control__inputWrapper--end'
65
+ ]);
66
+ const controlClasses = $derived(['control', error && 'control--isError', restProps.class ?? '']);
67
+ </script>
68
+
69
+ <div class={controlClasses}>
70
+ <Label class="control__label" for={id}>{label}</Label>
71
+ <div class={inputWrapperClasses}>
72
+ {#if start}
73
+ <div class="control__start">{@render start()}</div>
74
+ {/if}
75
+ {@render input({ inputProps })}
76
+ {#if end}
77
+ <div class="control__end">{@render end()}</div>
78
+ {/if}
79
+ </div>
80
+ {#if error}
81
+ <div class="control__error" id={`error-${id}`}>
82
+ <Icon Icon={IconX} class="control__errorIcon" />
83
+ {error.message}
84
+ </div>
85
+ {/if}
86
+ </div>
87
+
88
+ <style>
89
+ /* global required for children */
90
+ :global {
91
+ .control__inputWrapper--start input.input {
92
+ border-top-left-radius: 0;
93
+ border-bottom-left-radius: 0;
94
+ }
95
+ .control__inputWrapper--end input.input {
96
+ border-top-right-radius: 0;
97
+ border-bottom-right-radius: 0;
98
+ }
99
+ .control__erorIcon {
100
+ margin-top: 1px;
101
+ }
102
+ }
103
+ .control {
104
+ display: flex;
105
+ flex-direction: column;
106
+ gap: var(--size-1);
107
+ width: 100%;
108
+ }
109
+ .control__inputWrapper {
110
+ display: flex;
111
+ align-items: center;
112
+ width: 100%;
113
+ }
114
+ .control__start,
115
+ .control__end {
116
+ display: flex;
117
+ align-items: center;
118
+ height: var(--size-8);
119
+ border: var(--borderThin);
120
+ border-color: var(--inputBorderColor);
121
+ padding: 0 var(--size-3);
122
+ background: var(--contrastLow);
123
+ color: var(--fgMuted);
124
+ }
125
+ .control__start {
126
+ border-top-left-radius: var(--radius-2);
127
+ border-bottom-left-radius: var(--radius-2);
128
+ border-right: none;
129
+ }
130
+ .control__end {
131
+ border-top-right-radius: var(--radius-2);
132
+ border-bottom-right-radius: var(--radius-2);
133
+ border-left: none;
134
+ }
135
+ .control__error {
136
+ display: inline-flex;
137
+ gap: var(--size-1);
138
+ color: var(--fgDanger);
139
+ font-size: var(--font-size-1);
140
+ font-weight: var(--font-weight-6);
141
+ margin-top: var(--size-1);
142
+ border-radius: var(--size-2);
143
+ min-height: var(--size-7);
144
+ align-items: center;
145
+ }
146
+
147
+ :global(.control .control__label) {
148
+ display: block;
149
+ }
150
+ </style>
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ import { IconX } from '@tabler/icons-svelte';
3
+ import { Icon } from '../';
4
+ import { type $ZodIssue } from 'zod/v4/core';
5
+
6
+ type FormMutationResponse = {
7
+ success: boolean;
8
+ status: number;
9
+ message: string;
10
+ errors?: $ZodIssue[];
11
+ };
12
+ let { error }: { error?: FormMutationResponse } = $props();
13
+ </script>
14
+
15
+ {#if error}
16
+ <div class="formError" data-testid="formError">
17
+ <Icon Icon={IconX} class="formErrorIcon" />
18
+ <p>{error.message}</p>
19
+ </div>
20
+ {/if}
21
+
22
+ <style>
23
+ :global(.formErrorIcon) {
24
+ margin-top: 2px;
25
+ }
26
+ .formError {
27
+ display: flex;
28
+ gap: var(--size-1);
29
+ color: var(--fgDanger);
30
+ font-size: var(--font-size-1);
31
+ font-weight: var(--font-weight-6);
32
+ margin-top: 1rem;
33
+ border-radius: var(--size-2);
34
+ min-height: var(--size-7);
35
+ align-items: start;
36
+ }
37
+ </style>
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ import type { InputProps } from './types';
3
+ let {
4
+ value = $bindable(),
5
+ element = $bindable(),
6
+ variant = 'default',
7
+ hideAutocomplete,
8
+ ...restProps
9
+ }: InputProps = $props();
10
+
11
+ const inputClasses = $derived(['input', variant && `input--${variant}`, restProps.class ?? '']);
12
+ let autoCompleteOffAttrs = hideAutocomplete
13
+ ? {
14
+ autocomplete: 'off' as const,
15
+ 'data-1p-ignore': 'true' as const,
16
+ 'data-lpignore': 'true' as const
17
+ }
18
+ : {};
19
+ </script>
20
+
21
+ <input bind:this={element} bind:value {...restProps} {...autoCompleteOffAttrs} class={inputClasses} />
22
+
23
+ <style>
24
+ .input {
25
+ height: var(--size-8);
26
+ font-family: var(--font-sans);
27
+ border: var(--borderThin);
28
+ border-color: var(--inputBorderColor);
29
+ border-radius: var(--radius-2);
30
+ padding: 0 var(--size-3);
31
+ background: var(--inputBg);
32
+ width: 100%;
33
+ &:focus-visible {
34
+ outline: none;
35
+ border-color: var(--fg);
36
+ background: var(--inputFocusBg);
37
+ }
38
+ }
39
+ .input::placeholder {
40
+ color: var(--fgMuted);
41
+ }
42
+ .input--transparent {
43
+ border-color: transparent;
44
+ background: transparent;
45
+ &:hover {
46
+ border-color: var(--inputBorderColor);
47
+ }
48
+ }
49
+ .input[type='text'] {
50
+ cursor: text;
51
+ }
52
+ [data-fs-error],
53
+ [aria-invalid='true'] {
54
+ border-color: var(--fgDanger);
55
+ }
56
+ </style>