@tableslayer/ui 0.1.4 → 0.1.5

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 (206) hide show
  1. package/dist/components/PropsTable/PropsTable.svelte +1 -1
  2. package/package.json +9 -9
  3. package/src/lib/components/Avatar/Avatar.svelte +0 -82
  4. package/src/lib/components/Avatar/AvatarFileInput.svelte +0 -85
  5. package/src/lib/components/Avatar/AvatarPopover.svelte +0 -34
  6. package/src/lib/components/Avatar/index.ts +0 -4
  7. package/src/lib/components/Avatar/types.ts +0 -24
  8. package/src/lib/components/BrushSizeSlider/BrushSizeSlider.svelte +0 -174
  9. package/src/lib/components/BrushSizeSlider/index.ts +0 -1
  10. package/src/lib/components/Button/Button.svelte +0 -182
  11. package/src/lib/components/Button/ConfirmActionButton.svelte +0 -98
  12. package/src/lib/components/Button/IconButton.svelte +0 -121
  13. package/src/lib/components/Button/RadioButton.svelte +0 -93
  14. package/src/lib/components/Button/index.ts +0 -5
  15. package/src/lib/components/Button/types.ts +0 -54
  16. package/src/lib/components/CardFan/CardFan.svelte +0 -165
  17. package/src/lib/components/CardFan/index.ts +0 -2
  18. package/src/lib/components/CardFan/types.ts +0 -6
  19. package/src/lib/components/CodeBlock/Code.svelte +0 -7
  20. package/src/lib/components/CodeBlock/CodeBlock.svelte +0 -102
  21. package/src/lib/components/CodeBlock/index.ts +0 -3
  22. package/src/lib/components/CodeBlock/types.ts +0 -10
  23. package/src/lib/components/ColorMode/ColorMode.svelte +0 -8
  24. package/src/lib/components/ColorMode/index.ts +0 -2
  25. package/src/lib/components/ColorMode/types.ts +0 -12
  26. package/src/lib/components/ColorPicker/ColorPicker.svelte +0 -838
  27. package/src/lib/components/ColorPicker/ColorPickerSwatch.svelte +0 -32
  28. package/src/lib/components/ColorPicker/index.ts +0 -3
  29. package/src/lib/components/ColorPicker/types.ts +0 -51
  30. package/src/lib/components/ContextMenu/ContextMenu.svelte +0 -86
  31. package/src/lib/components/ContextMenu/index.ts +0 -2
  32. package/src/lib/components/ContextMenu/types.ts +0 -15
  33. package/src/lib/components/DrawingSliders/DrawingSliders.svelte +0 -379
  34. package/src/lib/components/DrawingSliders/index.ts +0 -1
  35. package/src/lib/components/Editor/Editor.svelte +0 -825
  36. package/src/lib/components/Editor/index.ts +0 -1
  37. package/src/lib/components/FogSliders/FogSliders.svelte +0 -33
  38. package/src/lib/components/FogSliders/index.ts +0 -1
  39. package/src/lib/components/Hr/Hr.svelte +0 -15
  40. package/src/lib/components/Hr/index.ts +0 -1
  41. package/src/lib/components/Icon/Icon.svelte +0 -6
  42. package/src/lib/components/Icon/index.ts +0 -2
  43. package/src/lib/components/Icon/types.ts +0 -20
  44. package/src/lib/components/Input/DualInputSlider.svelte +0 -126
  45. package/src/lib/components/Input/FileInput.svelte +0 -176
  46. package/src/lib/components/Input/FormControl.svelte +0 -150
  47. package/src/lib/components/Input/FormError.svelte +0 -37
  48. package/src/lib/components/Input/Input.svelte +0 -56
  49. package/src/lib/components/Input/InputCheckbox.svelte +0 -99
  50. package/src/lib/components/Input/InputSlider.svelte +0 -86
  51. package/src/lib/components/Input/Label.svelte +0 -19
  52. package/src/lib/components/Input/index.ts +0 -9
  53. package/src/lib/components/Input/types.ts +0 -39
  54. package/src/lib/components/Link/Link.svelte +0 -41
  55. package/src/lib/components/Link/LinkBox.svelte +0 -20
  56. package/src/lib/components/Link/LinkOverlay.svelte +0 -23
  57. package/src/lib/components/Link/index.ts +0 -4
  58. package/src/lib/components/Link/types.ts +0 -17
  59. package/src/lib/components/Loading/Loader.svelte +0 -60
  60. package/src/lib/components/Loading/Skeleton.svelte +0 -9
  61. package/src/lib/components/Loading/index.ts +0 -2
  62. package/src/lib/components/Logo/Logo.svelte +0 -16
  63. package/src/lib/components/Logo/index.ts +0 -1
  64. package/src/lib/components/MarkerTooltip/MarkerTooltip.svelte +0 -435
  65. package/src/lib/components/MarkerTooltip/index.ts +0 -1
  66. package/src/lib/components/Menu/SelectorMenu.svelte +0 -280
  67. package/src/lib/components/Menu/index.ts +0 -2
  68. package/src/lib/components/Menu/types.ts +0 -17
  69. package/src/lib/components/MyCounterButton.svelte +0 -11
  70. package/src/lib/components/Panel/index.ts +0 -2
  71. package/src/lib/components/Panel/panel.svelte +0 -18
  72. package/src/lib/components/Panel/types.ts +0 -8
  73. package/src/lib/components/PersistButton/PersistButton.svelte +0 -100
  74. package/src/lib/components/PersistButton/index.ts +0 -1
  75. package/src/lib/components/Popover/Popover.svelte +0 -81
  76. package/src/lib/components/Popover/index.ts +0 -2
  77. package/src/lib/components/Popover/types.ts +0 -19
  78. package/src/lib/components/PropsTable/PropsTable.svelte +0 -107
  79. package/src/lib/components/RadialMenu/EffectPreview.svelte +0 -36
  80. package/src/lib/components/RadialMenu/EffectPreviewScene.svelte +0 -194
  81. package/src/lib/components/RadialMenu/RadialMenu.svelte +0 -503
  82. package/src/lib/components/RadialMenu/RadialMenuItem.svelte +0 -176
  83. package/src/lib/components/RadialMenu/index.ts +0 -2
  84. package/src/lib/components/RadialMenu/types.ts +0 -35
  85. package/src/lib/components/Select/Select.svelte +0 -342
  86. package/src/lib/components/Select/index.ts +0 -2
  87. package/src/lib/components/Select/types.ts +0 -22
  88. package/src/lib/components/Spacer/Spacer.svelte +0 -14
  89. package/src/lib/components/Spacer/index.ts +0 -2
  90. package/src/lib/components/Spacer/types.ts +0 -5
  91. package/src/lib/components/Stage/components/AnnotationLayer/AnnotationLayer.svelte +0 -445
  92. package/src/lib/components/Stage/components/AnnotationLayer/AnnotationMaterial.svelte +0 -167
  93. package/src/lib/components/Stage/components/AnnotationLayer/types.ts +0 -196
  94. package/src/lib/components/Stage/components/CursorLayer/CursorLayer.svelte +0 -148
  95. package/src/lib/components/Stage/components/CursorLayer/cursor.svg +0 -26
  96. package/src/lib/components/Stage/components/CursorLayer/index.ts +0 -2
  97. package/src/lib/components/Stage/components/CursorLayer/types.ts +0 -23
  98. package/src/lib/components/Stage/components/DrawingLayer/DrawingMaterial.svelte +0 -364
  99. package/src/lib/components/Stage/components/DrawingLayer/types.ts +0 -65
  100. package/src/lib/components/Stage/components/EdgeOverlayLayer/EdgeOverlayLayer.svelte +0 -72
  101. package/src/lib/components/Stage/components/EdgeOverlayLayer/types.ts +0 -34
  102. package/src/lib/components/Stage/components/FogLayer/FogLayer.svelte +0 -75
  103. package/src/lib/components/Stage/components/FogLayer/types.ts +0 -51
  104. package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarLayer.svelte +0 -249
  105. package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarMaterial.svelte +0 -200
  106. package/src/lib/components/Stage/components/FogOfWarLayer/types.ts +0 -116
  107. package/src/lib/components/Stage/components/GridLayer/GridLayer.svelte +0 -20
  108. package/src/lib/components/Stage/components/GridLayer/GridMaterial.svelte +0 -69
  109. package/src/lib/components/Stage/components/GridLayer/types.ts +0 -79
  110. package/src/lib/components/Stage/components/LayerInput/LayerInput.svelte +0 -300
  111. package/src/lib/components/Stage/components/MapLayer/MapLayer.svelte +0 -196
  112. package/src/lib/components/Stage/components/MapLayer/dataSources/GifDataSource.ts +0 -265
  113. package/src/lib/components/Stage/components/MapLayer/dataSources/IMapDataSource.ts +0 -55
  114. package/src/lib/components/Stage/components/MapLayer/dataSources/ImageDataSource.ts +0 -87
  115. package/src/lib/components/Stage/components/MapLayer/dataSources/VideoDataSource.ts +0 -150
  116. package/src/lib/components/Stage/components/MapLayer/dataSources/dataSourceFactory.ts +0 -48
  117. package/src/lib/components/Stage/components/MapLayer/dataSources/index.ts +0 -16
  118. package/src/lib/components/Stage/components/MapLayer/types.ts +0 -58
  119. package/src/lib/components/Stage/components/MarkerLayer/MarkerLayer.svelte +0 -398
  120. package/src/lib/components/Stage/components/MarkerLayer/MarkerToken.svelte +0 -262
  121. package/src/lib/components/Stage/components/MarkerLayer/types.ts +0 -126
  122. package/src/lib/components/Stage/components/MeasurementLayer/MeasurementLayer.svelte +0 -364
  123. package/src/lib/components/Stage/components/MeasurementLayer/MeasurementManager.svelte +0 -473
  124. package/src/lib/components/Stage/components/MeasurementLayer/measurements/BaseMeasurement.ts +0 -427
  125. package/src/lib/components/Stage/components/MeasurementLayer/measurements/BeamMeasurement.ts +0 -105
  126. package/src/lib/components/Stage/components/MeasurementLayer/measurements/CircleMeasurement.ts +0 -98
  127. package/src/lib/components/Stage/components/MeasurementLayer/measurements/ConeMeasurement.ts +0 -163
  128. package/src/lib/components/Stage/components/MeasurementLayer/measurements/LineMeasurement.ts +0 -102
  129. package/src/lib/components/Stage/components/MeasurementLayer/measurements/RectangleMeasurement.ts +0 -120
  130. package/src/lib/components/Stage/components/MeasurementLayer/measurements/index.ts +0 -7
  131. package/src/lib/components/Stage/components/MeasurementLayer/types.ts +0 -94
  132. package/src/lib/components/Stage/components/MeasurementLayer/utils/canvasDrawing.ts +0 -357
  133. package/src/lib/components/Stage/components/MeasurementLayer/utils/distanceCalculations.ts +0 -170
  134. package/src/lib/components/Stage/components/ParticleSystem/ParticleSystem.svelte +0 -220
  135. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/ash.png +0 -0
  136. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/leaves.png +0 -0
  137. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/rain.png +0 -0
  138. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/snow.png +0 -0
  139. package/src/lib/components/Stage/components/ParticleSystem/rng.js +0 -20
  140. package/src/lib/components/Stage/components/ParticleSystem/types.ts +0 -95
  141. package/src/lib/components/Stage/components/PerformanceDebugger/PerformanceDebugger.svelte +0 -144
  142. package/src/lib/components/Stage/components/PerformanceDebugger/index.ts +0 -1
  143. package/src/lib/components/Stage/components/PerformanceOverlay/PerformanceOverlay.svelte +0 -208
  144. package/src/lib/components/Stage/components/PerformanceOverlay/index.ts +0 -1
  145. package/src/lib/components/Stage/components/PointerInputManager/PointerInputManager.svelte +0 -201
  146. package/src/lib/components/Stage/components/Scene/Scene.svelte +0 -651
  147. package/src/lib/components/Stage/components/Scene/luts.ts +0 -24
  148. package/src/lib/components/Stage/components/Scene/types.ts +0 -225
  149. package/src/lib/components/Stage/components/Stage/Stage.svelte +0 -332
  150. package/src/lib/components/Stage/components/Stage/types.ts +0 -136
  151. package/src/lib/components/Stage/components/WeatherLayer/WeatherLayer.svelte +0 -135
  152. package/src/lib/components/Stage/components/WeatherLayer/presets/AshPreset.ts +0 -71
  153. package/src/lib/components/Stage/components/WeatherLayer/presets/LeavesPreset.ts +0 -70
  154. package/src/lib/components/Stage/components/WeatherLayer/presets/RainPreset.ts +0 -68
  155. package/src/lib/components/Stage/components/WeatherLayer/presets/SnowPreset.ts +0 -70
  156. package/src/lib/components/Stage/components/WeatherLayer/presets/index.ts +0 -6
  157. package/src/lib/components/Stage/components/WeatherLayer/types.ts +0 -35
  158. package/src/lib/components/Stage/helpers/clippingPlaneStore.svelte.ts +0 -28
  159. package/src/lib/components/Stage/helpers/debugState.svelte.ts +0 -18
  160. package/src/lib/components/Stage/helpers/grid.ts +0 -548
  161. package/src/lib/components/Stage/helpers/lazyBrush.ts +0 -171
  162. package/src/lib/components/Stage/helpers/performanceMetrics.svelte.ts +0 -220
  163. package/src/lib/components/Stage/helpers/utils.ts +0 -21
  164. package/src/lib/components/Stage/index.ts +0 -49
  165. package/src/lib/components/Stage/shaders/AnnotationEffects.frag +0 -1070
  166. package/src/lib/components/Stage/shaders/Annotations.frag +0 -29
  167. package/src/lib/components/Stage/shaders/Drawing.frag +0 -83
  168. package/src/lib/components/Stage/shaders/Drawing.vert +0 -5
  169. package/src/lib/components/Stage/shaders/Fog.frag +0 -147
  170. package/src/lib/components/Stage/shaders/FractalNoise.frag +0 -96
  171. package/src/lib/components/Stage/shaders/GridShader.frag +0 -174
  172. package/src/lib/components/Stage/shaders/Overlay.frag +0 -23
  173. package/src/lib/components/Stage/shaders/Overlay.vert +0 -0
  174. package/src/lib/components/Stage/shaders/Particles.frag +0 -27
  175. package/src/lib/components/Stage/shaders/Particles.vert +0 -51
  176. package/src/lib/components/Stage/shaders/ToolOutline.frag +0 -59
  177. package/src/lib/components/Stage/shaders/default.vert +0 -8
  178. package/src/lib/components/Stage/types.ts +0 -4
  179. package/src/lib/components/Table/Table.svelte +0 -16
  180. package/src/lib/components/Table/Td.svelte +0 -17
  181. package/src/lib/components/Table/Th.svelte +0 -18
  182. package/src/lib/components/Table/index.ts +0 -4
  183. package/src/lib/components/Table/types.ts +0 -14
  184. package/src/lib/components/Text/Text.svelte +0 -23
  185. package/src/lib/components/Text/index.ts +0 -2
  186. package/src/lib/components/Text/types.ts +0 -12
  187. package/src/lib/components/Title/Title.svelte +0 -54
  188. package/src/lib/components/Title/index.ts +0 -2
  189. package/src/lib/components/Title/types.ts +0 -9
  190. package/src/lib/components/Toast/Toast.svelte +0 -155
  191. package/src/lib/components/Toast/index.ts +0 -5
  192. package/src/lib/components/Toast/toastCookie.ts +0 -24
  193. package/src/lib/components/Toast/types.ts +0 -6
  194. package/src/lib/components/ToolTip/ToolTip.svelte +0 -70
  195. package/src/lib/components/ToolTip/index.ts +0 -2
  196. package/src/lib/components/ToolTip/types.ts +0 -14
  197. package/src/lib/components/index.ts +0 -32
  198. package/src/lib/components/types.ts +0 -0
  199. package/src/lib/index.ts +0 -2
  200. package/src/lib/styles/globals.css +0 -108
  201. package/src/lib/styles/normalize.css +0 -9
  202. package/src/lib/styles/reset.css +0 -133
  203. package/src/lib/styles/utilities.css +0 -179
  204. package/src/lib/styles/vars.css +0 -1103
  205. package/src/lib/types/awareness.ts +0 -17
  206. package/src/lib/utils/rle.ts +0 -217
@@ -1,825 +0,0 @@
1
- <script lang="ts">
2
- import { onMount, onDestroy } from 'svelte';
3
- import { Editor, type JSONContent } from '@tiptap/core';
4
- import StarterKit from '@tiptap/starter-kit';
5
- import { Link } from '@tiptap/extension-link';
6
- import Typography from '@tiptap/extension-typography';
7
- import {
8
- IconBold,
9
- IconList,
10
- IconListNumbers,
11
- IconItalic,
12
- IconLink,
13
- IconSelector,
14
- IconQuoteFilled,
15
- IconCheck
16
- } from '@tabler/icons-svelte';
17
- import { Icon } from '../Icon';
18
- import { Popover } from '../Popover';
19
- import { Button } from '../Button';
20
- import { Input } from '../Input';
21
- import { computePosition, autoUpdate, offset, shift, flip } from '@floating-ui/dom';
22
- import { IconButton } from '../Button';
23
-
24
- let {
25
- height = 'auto',
26
- content = $bindable(undefined),
27
- debug = false,
28
- editable = true,
29
- onChange
30
- }: {
31
- height?: number | string;
32
- content?: JSONContent | null | undefined;
33
- debug?: boolean;
34
- editable?: boolean;
35
- onChange?: () => void;
36
- } = $props();
37
-
38
- let element: HTMLDivElement | undefined = $state();
39
- let editor: Editor | undefined = $state();
40
- let editorReady = $state(false);
41
-
42
- // Since we're now only using JSON content, this is no longer needed
43
- // const isJsonContent = content !== null && content !== undefined && typeof content === 'object';
44
-
45
- // We'll always use JSON, but for debug purposes we'll keep track of HTML
46
- let editorHtml = $state('');
47
-
48
- // Create explicit state variables for each button's active state
49
- let isBold = $state(false);
50
- let isItalic = $state(false);
51
- let isLink = $state(false);
52
- let isParagraph = $state(false);
53
- let isH1 = $state(false);
54
- let isH2 = $state(false);
55
- let isH3 = $state(false);
56
- let isBulletList = $state(false);
57
- let isOrderedList = $state(false);
58
- let isBlockquote = $state(false);
59
-
60
- // Link popover state
61
- let linkPopoverVisible = $state(false);
62
- let linkPopoverElement: HTMLDivElement | undefined = $state();
63
- let linkInputElement: HTMLInputElement | undefined = $state();
64
- let currentLinkElement: HTMLAnchorElement | null = $state(null);
65
- let currentLinkUrl = $state('');
66
- let cleanupAutoUpdate: (() => void) | undefined;
67
- let portalContainer: HTMLDivElement | undefined = $state();
68
-
69
- // Create portal container in body when component is mounted
70
- function createPortalContainer() {
71
- // Check if portal container already exists
72
- const existingContainer = document.getElementById('editorLinkPortal');
73
- if (existingContainer) {
74
- portalContainer = existingContainer as HTMLDivElement;
75
- return;
76
- }
77
-
78
- // Create a new portal container
79
- const container = document.createElement('div');
80
- container.id = 'editorLinkPortal';
81
- document.body.appendChild(container);
82
- portalContainer = container;
83
- }
84
-
85
- const textType = $derived.by(() => {
86
- if (isH1) return 'Huge';
87
- if (isH2) return 'Large';
88
- if (isH3) return 'Medium';
89
- if (isParagraph) return 'Normal';
90
- return '';
91
- });
92
-
93
- // Function to update all active states
94
- function updateActiveStates() {
95
- if (!editor) return;
96
-
97
- isBold = editor.isActive('bold');
98
- isItalic = editor.isActive('italic');
99
- isLink = editor.isActive('link');
100
- isParagraph = editor.isActive('paragraph');
101
- isH1 = editor.isActive('heading', { level: 1 });
102
- isH2 = editor.isActive('heading', { level: 2 });
103
- isH3 = editor.isActive('heading', { level: 3 });
104
- isBlockquote = editor.isActive('blockquote');
105
- isBulletList = editor.isActive('bulletList');
106
- isOrderedList = editor.isActive('orderedList');
107
- }
108
-
109
- // Effect to update editor content when it changes externally
110
- $effect(() => {
111
- if (editor && editorReady && content !== undefined) {
112
- // Get current editor content
113
- const currentContent = editor.getJSON();
114
-
115
- // Only update if content actually changed (avoid infinite loops)
116
- if (JSON.stringify(currentContent) !== JSON.stringify(content)) {
117
- // Update editor with new content from outside
118
- editor.commands.setContent(content || { type: 'doc', content: [{ type: 'paragraph' }] });
119
- }
120
- }
121
- });
122
-
123
- onMount(() => {
124
- // Create portal container
125
- createPortalContainer();
126
-
127
- // Default empty content
128
- const emptyContent = { type: 'doc', content: [{ type: 'paragraph' }] };
129
-
130
- // Prepare content - always ensure we have valid JSON structure
131
- let initialContent;
132
-
133
- if (content === null || content === undefined) {
134
- // Use empty content if nothing provided
135
- initialContent = emptyContent;
136
- } else if (typeof content === 'string') {
137
- // If string was passed (shouldn't happen but just in case), convert to JSON
138
- try {
139
- // First, try to parse it as JSON string
140
- initialContent = JSON.parse(content);
141
- } catch (e) {
142
- console.log(e);
143
- // If it's plain HTML, initialize with it
144
- initialContent = content;
145
- // And immediately update our bound content to be JSON
146
- setTimeout(() => {
147
- if (editor) {
148
- content = editor.getJSON();
149
- }
150
- }, 0);
151
- }
152
- } else {
153
- // Content is already JSON object
154
- initialContent = content;
155
- }
156
-
157
- editor = new Editor({
158
- editable,
159
- element: element,
160
- extensions: [
161
- StarterKit,
162
- Typography,
163
- Link.configure({
164
- openOnClick: false,
165
- HTMLAttributes: {
166
- class: 'editor-link'
167
- }
168
- })
169
- ],
170
- content: initialContent, // Pass content with fallback for null/undefined
171
- onSelectionUpdate: () => {
172
- updateActiveStates();
173
- },
174
- onUpdate: ({ editor }) => {
175
- // Always update content as JSON
176
- const newJson = editor.getJSON();
177
- content = newJson;
178
- // Update HTML for debug purposes only
179
- editorHtml = editor.getHTML();
180
- updateActiveStates();
181
-
182
- // Call onChange callback if provided
183
- if (onChange) {
184
- onChange();
185
- }
186
- },
187
- onTransaction: () => {
188
- updateActiveStates();
189
- }
190
- });
191
-
192
- if (element) {
193
- element.addEventListener('click', handleEditorClick, true);
194
- element.addEventListener('mousedown', handleEditorMouseDown, true);
195
- }
196
-
197
- editorReady = true;
198
- updateActiveStates();
199
- });
200
-
201
- onDestroy(() => {
202
- if (editor) {
203
- editor.destroy();
204
- }
205
-
206
- if (element) {
207
- element.removeEventListener('click', handleEditorClick, true);
208
- element.removeEventListener('mousedown', handleEditorMouseDown, true);
209
- }
210
-
211
- // Clean up floating UI positioning if active
212
- if (cleanupAutoUpdate) {
213
- cleanupAutoUpdate();
214
- }
215
-
216
- // Clean up the popover element from the portal container
217
- if (linkPopoverElement && portalContainer && portalContainer.contains(linkPopoverElement)) {
218
- portalContainer.removeChild(linkPopoverElement);
219
- }
220
-
221
- // Remove portal container if it exists and we are the last editor instance
222
- if (portalContainer && portalContainer.childNodes.length === 0) {
223
- if (document.body.contains(portalContainer)) {
224
- document.body.removeChild(portalContainer);
225
- }
226
- }
227
- });
228
-
229
- function handleEditorClick(e: MouseEvent) {
230
- const target = e.target as HTMLElement;
231
- const linkElement = target.closest('a');
232
-
233
- if (linkElement && linkElement.classList.contains('editor-link')) {
234
- e.preventDefault();
235
- e.stopPropagation();
236
- e.stopImmediatePropagation();
237
-
238
- hideLinkPopover();
239
-
240
- currentLinkElement = linkElement;
241
- currentLinkUrl = linkElement.getAttribute('href') || '';
242
- showLinkPopover(linkElement);
243
- return false;
244
- } else if (!linkElement) {
245
- hideLinkPopover();
246
- }
247
- }
248
-
249
- function handleEditorMouseDown(e: MouseEvent) {
250
- const target = e.target as HTMLElement;
251
- const linkElement = target.closest('a');
252
-
253
- if (linkElement && linkElement.classList.contains('editor-link')) {
254
- e.preventDefault();
255
- e.stopPropagation();
256
- return false;
257
- }
258
- }
259
-
260
- // This function is used by the <svelte:document> event handler
261
- function handleDocumentClick(e: MouseEvent) {
262
- // Only close if clicking outside the popover and link
263
- if (linkPopoverVisible) {
264
- const target = e.target as HTMLElement;
265
- if (
266
- linkPopoverElement &&
267
- !linkPopoverElement.contains(target) &&
268
- currentLinkElement !== target &&
269
- !currentLinkElement?.contains(target) &&
270
- !target.closest('.editor__btn') && // Don't close when clicking toolbar buttons
271
- // Also check if we're clicking inside the editor but not on a link
272
- !(element?.contains(target) && !target.closest('a'))
273
- ) {
274
- hideLinkPopover();
275
- }
276
- }
277
- }
278
-
279
- function getSelectionCoordinates(): { left: number; bottom: number } | null {
280
- if (!editor) return null;
281
-
282
- const view = editor.view;
283
- const { from, to } = view.state.selection;
284
-
285
- if (from === to) return null; // No selection
286
-
287
- // Get the DOM range for the current selection
288
- const start = view.coordsAtPos(from);
289
- const end = view.coordsAtPos(to);
290
-
291
- return {
292
- left: (start.left + end.left) / 2,
293
- bottom: Math.max(start.bottom, end.bottom)
294
- };
295
- }
296
-
297
- function showLinkPopover(anchorElement: HTMLElement) {
298
- if (!linkPopoverElement || !portalContainer) return;
299
-
300
- // Move the popover element to the portal container if it's not already there
301
- if (!portalContainer.contains(linkPopoverElement)) {
302
- // Create a new popover element in the portal container
303
- const popoverClone = linkPopoverElement.cloneNode(true) as HTMLDivElement;
304
- portalContainer.appendChild(popoverClone);
305
-
306
- // Update our reference to the new popover element
307
- linkPopoverElement = popoverClone;
308
-
309
- // Find the input element within the cloned popover
310
- linkInputElement = popoverClone.querySelector('.linkPopover__input') as HTMLInputElement;
311
-
312
- // Setup event handlers for the portal popover
313
- setupPortalPopoverEvents();
314
- }
315
-
316
- // Always update the input field value with the current link URL
317
- if (linkInputElement) {
318
- linkInputElement.value = currentLinkUrl;
319
- }
320
-
321
- linkPopoverVisible = true;
322
-
323
- // Position the popover using Floating UI
324
- if (cleanupAutoUpdate) {
325
- cleanupAutoUpdate();
326
- }
327
-
328
- cleanupAutoUpdate = autoUpdate(anchorElement, linkPopoverElement, () => {
329
- computePosition(anchorElement, linkPopoverElement!, {
330
- placement: 'bottom',
331
- middleware: [offset(8), shift({ padding: 10 }), flip()],
332
- strategy: 'fixed'
333
- }).then(({ x, y }) => {
334
- if (linkPopoverElement) {
335
- Object.assign(linkPopoverElement.style, {
336
- left: `${x}px`,
337
- top: `${y}px`,
338
- position: 'fixed',
339
- display: 'block',
340
- transform: 'translateZ(0)'
341
- });
342
- }
343
- });
344
- });
345
-
346
- // Focus the input field if we're editing
347
- setTimeout(() => {
348
- if (linkInputElement) {
349
- linkInputElement.focus();
350
- linkInputElement.select();
351
- }
352
- }, 10);
353
- }
354
-
355
- function hideLinkPopover() {
356
- linkPopoverVisible = false;
357
- currentLinkElement = null;
358
- currentLinkUrl = '';
359
-
360
- if (cleanupAutoUpdate) {
361
- cleanupAutoUpdate();
362
- cleanupAutoUpdate = undefined;
363
- }
364
-
365
- // Hide the popover in the portal if it exists
366
- if (linkPopoverElement && portalContainer && portalContainer.contains(linkPopoverElement)) {
367
- linkPopoverElement.style.display = 'none';
368
- }
369
- }
370
-
371
- function addOrUpdateLink() {
372
- if (!editor) return;
373
-
374
- if (currentLinkUrl.trim() === '') {
375
- removeLink();
376
- return;
377
- }
378
-
379
- try {
380
- // If we're updating an existing link, we need to select it first
381
- if (currentLinkElement && currentLinkElement.tagName === 'A') {
382
- const pos = editor.view.posAtDOM(currentLinkElement, 0);
383
- const end = pos + currentLinkElement.textContent!.length;
384
- editor.chain().focus().setTextSelection({ from: pos, to: end }).run();
385
- }
386
-
387
- editor.chain().focus().extendMarkRange('link').setLink({ href: currentLinkUrl }).run();
388
- hideLinkPopover();
389
- } catch (e) {
390
- console.error(e);
391
- }
392
- }
393
-
394
- function removeLink() {
395
- if (!editor) return;
396
-
397
- if (currentLinkElement && currentLinkElement.tagName === 'A') {
398
- const pos = editor.view.posAtDOM(currentLinkElement, 0);
399
- const end = pos + currentLinkElement.textContent!.length;
400
- editor.chain().focus().setTextSelection({ from: pos, to: end }).unsetLink().run();
401
- } else {
402
- editor.chain().focus().extendMarkRange('link').unsetLink().run();
403
- }
404
-
405
- hideLinkPopover();
406
- }
407
-
408
- function visitLink() {
409
- if (currentLinkUrl) {
410
- window.open(currentLinkUrl, '_blank');
411
- hideLinkPopover();
412
- }
413
- }
414
-
415
- // Setup portal popover event handlers when the component is mounted
416
- function setupPortalPopoverEvents() {
417
- if (!linkPopoverElement || !portalContainer) return;
418
-
419
- // Add event handlers for the buttons in the portal popover
420
- const confirmButton = linkPopoverElement.querySelector('.linkPopover__inputRow button') as HTMLElement;
421
- if (confirmButton) {
422
- // Use the correct typing approach for TypeScript with Svelte 5
423
- (confirmButton as unknown as { onclick: typeof addOrUpdateLink }).onclick = addOrUpdateLink;
424
- }
425
-
426
- // Add event handlers for the visit and remove buttons
427
- const buttons = linkPopoverElement.querySelectorAll('.linkPopover__actions button');
428
- if (buttons.length >= 2) {
429
- (buttons[0] as unknown as { onclick: typeof visitLink }).onclick = visitLink;
430
- (buttons[1] as unknown as { onclick: typeof removeLink }).onclick = removeLink;
431
- }
432
-
433
- if (linkInputElement) {
434
- // Set up keydown handler for Enter key
435
- (linkInputElement as unknown as { onkeydown: typeof handleLinkInputKeydown }).onkeydown = handleLinkInputKeydown;
436
-
437
- // Set up input handler to keep our state variable in sync
438
- linkInputElement.addEventListener('input', (e: Event) => {
439
- currentLinkUrl = (e.target as HTMLInputElement).value;
440
- });
441
- }
442
- }
443
-
444
- function setNewLink() {
445
- if (!editor) return;
446
-
447
- // For creating a new link (toolbar button)
448
- hideLinkPopover(); // Close any existing popover first
449
-
450
- const selection = editor.view.state.selection;
451
- const hasSelection = !selection.empty;
452
-
453
- if (hasSelection) {
454
- // With selection, first create the link with a temporary URL
455
- currentLinkUrl = '';
456
- editor.chain().focus().extendMarkRange('link').setLink({ href: 'https://' }).run();
457
-
458
- // Now find the newly created link
459
- setTimeout(() => {
460
- // Get coordinates of the selection
461
- const coords = getSelectionCoordinates();
462
- if (!coords) return;
463
-
464
- // Create a temp anchor element for positioning the popover
465
- const tempElement = document.createElement('span');
466
- tempElement.style.position = 'absolute';
467
- tempElement.style.left = `${coords.left}px`;
468
- tempElement.style.top = `${coords.bottom}px`;
469
- document.body.appendChild(tempElement);
470
-
471
- currentLinkElement = tempElement as unknown as HTMLAnchorElement;
472
- showLinkPopover(tempElement);
473
-
474
- // Clean up the temporary element after we're done
475
- setTimeout(() => {
476
- if (document.body.contains(tempElement)) {
477
- document.body.removeChild(tempElement);
478
- }
479
- }, 100);
480
- }, 10);
481
- } else {
482
- // For empty selection, insert a placeholder link
483
- currentLinkUrl = 'https://';
484
-
485
- // Insert link with default text
486
- editor.chain().focus().insertContent('<a href="https://">link</a>').run();
487
-
488
- // Find the newly inserted link and show popover
489
- setTimeout(() => {
490
- if (!element) return;
491
-
492
- const links = element.querySelectorAll('a[href="https://"]');
493
- if (links && links.length > 0) {
494
- currentLinkElement = links[links.length - 1] as HTMLAnchorElement;
495
- showLinkPopover(currentLinkElement);
496
- }
497
- }, 10);
498
- }
499
- }
500
-
501
- // Handle enter key in the link input
502
- function handleLinkInputKeydown(e: KeyboardEvent) {
503
- if (e.key === 'Enter') {
504
- e.preventDefault();
505
- addOrUpdateLink();
506
- }
507
- }
508
- </script>
509
-
510
- <svelte:document on:click={handleDocumentClick} />
511
-
512
- <div class={['editor', !editable && 'editor--notEdtiable']} style={`height: ${height}; max-height: ${height}`}>
513
- {#if editable}
514
- <div class="editor__toolbar">
515
- {#if editor}
516
- <button
517
- onclick={() => editor?.chain().focus().toggleBold().run()}
518
- class={['editor__btn', isBold && 'isActive']}
519
- >
520
- <Icon Icon={IconBold} size="20px" stroke={2} />
521
- </button>
522
- <button
523
- onclick={() => editor?.chain().focus().toggleItalic().run()}
524
- class={['editor__btn', isItalic && 'isActive']}
525
- >
526
- <Icon Icon={IconItalic} size="20px" stroke={2} />
527
- </button>
528
- <button onclick={setNewLink} class={['editor__btn', isLink && 'isActive']}>
529
- <Icon Icon={IconLink} size="20px" stroke={2} />
530
- </button>
531
- <button
532
- onclick={() => editor?.chain().focus().toggleBulletList().run()}
533
- class={['editor__btn', isBulletList && 'isActive']}
534
- >
535
- <Icon Icon={IconList} size="20px" stroke={2} />
536
- </button>
537
- <button
538
- onclick={() => editor?.chain().focus().toggleOrderedList().run()}
539
- class={['editor__btn', isOrderedList && 'isActive']}
540
- >
541
- <Icon Icon={IconListNumbers} size="20px" stroke={2} />
542
- </button>
543
- <button
544
- onclick={() => editor?.chain().focus().toggleBlockquote().run()}
545
- class={['editor__btn', isBlockquote && 'isActive']}
546
- >
547
- <Icon Icon={IconQuoteFilled} size="20px" stroke={2} />
548
- </button>
549
- <Popover>
550
- {#snippet trigger()}
551
- <div class="editor__toolbarTrigger">
552
- {textType}
553
- <Icon Icon={IconSelector} size="1rem" color="var(--fgMuted)" />
554
- </div>
555
- {/snippet}
556
- {#snippet content()}
557
- <div class="editor__toolbarTextOptions">
558
- <button
559
- onclick={() => editor?.chain().focus().setParagraph().run()}
560
- class:active={['editor__toolbarTextP', isParagraph && 'isActive']}
561
- >
562
- Normal
563
- </button>
564
- <button
565
- onclick={() => editor?.chain().focus().toggleHeading({ level: 3 }).run()}
566
- class={['editor__toolbarTextH3', isH3 && 'isActive']}
567
- >
568
- Medium
569
- </button>
570
- <button
571
- onclick={() => editor?.chain().focus().toggleHeading({ level: 2 }).run()}
572
- class={['editor__toolbarTextH2', isH2 && 'isActive']}
573
- >
574
- Large
575
- </button>
576
- <button
577
- onclick={() => editor?.chain().focus().toggleHeading({ level: 1 }).run()}
578
- class={['editor__toolbarTextH1', isH1 && 'isActive']}
579
- >
580
- Huge
581
- </button>
582
- </div>
583
- {/snippet}
584
- </Popover>
585
- {/if}
586
- </div>
587
- {/if}
588
-
589
- <div class="editor__content">
590
- <div bind:this={element}></div>
591
- </div>
592
-
593
- <!-- Link Popover Template (hidden) - Will be cloned to portal container -->
594
- <div bind:this={linkPopoverElement} class="linkPopover" style="display: none;">
595
- <div class="linkPopover__content">
596
- <div class="linkPopover__inputRow">
597
- <Input
598
- bind:element={linkInputElement}
599
- type="text"
600
- bind:value={currentLinkUrl}
601
- placeholder="https://"
602
- class="linkPopover__input"
603
- onkeydown={handleLinkInputKeydown}
604
- />
605
- <IconButton onclick={addOrUpdateLink}>
606
- <Icon Icon={IconCheck} stroke={2} />
607
- </IconButton>
608
- </div>
609
- <div class="linkPopover__actions">
610
- <Button onclick={visitLink}>Go to link</Button>
611
- <Button variant="danger" onclick={removeLink}>Remove</Button>
612
- </div>
613
- </div>
614
- </div>
615
-
616
- {#if debug}
617
- <div class="editor__state">
618
- <h4>Editor Status: {editorReady ? 'Ready' : 'Loading...'}</h4>
619
- <h4>Content Type: JSON</h4>
620
- {#if content === null || content === undefined}
621
- <pre>null/undefined</pre>
622
- {:else}
623
- <pre>{JSON.stringify(content, null, 2)}</pre>
624
- {/if}
625
-
626
- <h4>HTML representation:</h4>
627
- <pre>{editorHtml}</pre>
628
- </div>
629
- {/if}
630
- </div>
631
-
632
- <style>
633
- .editor {
634
- position: relative;
635
- width: 100%;
636
- border: var(--borderThin);
637
- border-radius: var(--radius-2);
638
- overflow-y: auto;
639
- }
640
- .editor.editor--notEdtiable {
641
- pointer-events: none;
642
- border: none;
643
- border-radius: 0;
644
- }
645
- .editor.editor--notEdtiable .editor__content {
646
- pointer-events: none;
647
- padding: 0;
648
- }
649
- .editor__toolbar {
650
- position: sticky;
651
- top: 0;
652
- background-color: var(--bg);
653
- z-index: 2;
654
- display: flex;
655
- gap: 0.25rem;
656
- padding: 0.5rem 0.5rem;
657
- }
658
- .editor__content {
659
- padding: 1rem;
660
- position: relative;
661
- }
662
- .editor__state {
663
- padding: 1rem;
664
- border-top: var(--borderThin);
665
- background-color: var(--contrastLow);
666
- font-size: 0.85rem;
667
- }
668
- .editor__state pre {
669
- max-height: 150px;
670
- overflow-y: auto;
671
- background-color: var(--contrastMedium);
672
- padding: 0.5rem;
673
- border-radius: var(--radius-2);
674
- }
675
- .editor__btn {
676
- border: solid 2px transparent;
677
- border-radius: var(--radius-2);
678
- display: flex;
679
- align-items: center;
680
- justify-content: center;
681
- cursor: pointer;
682
- height: 2rem;
683
- width: 2rem;
684
- }
685
- .editor__btn.isActive {
686
- border-color: var(--fg);
687
- }
688
- .editor__btn:hover {
689
- background-color: var(--iconBtn-bgHover);
690
- border: var(--iconBtn-borderHover);
691
- }
692
-
693
- .linkPopover {
694
- position: fixed;
695
- z-index: 9999;
696
- filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1));
697
- will-change: transform;
698
- }
699
- .linkPopover__content {
700
- box-shadow: var(--shadow-1);
701
- background-color: var(--bg);
702
- border: var(--borderThin);
703
- border-radius: var(--radius-2);
704
- padding: 0.5rem;
705
- width: 280px;
706
- }
707
- .linkPopover__inputRow {
708
- display: flex;
709
- gap: 0.5rem;
710
- margin-bottom: 0.75rem;
711
- }
712
-
713
- :global(#editorLinkPortal) {
714
- position: fixed;
715
- top: 0;
716
- left: 0;
717
- width: 0;
718
- height: 0;
719
- z-index: 9999;
720
- pointer-events: none;
721
- }
722
- :global(#editorLinkPortal .linkPopover) {
723
- pointer-events: auto;
724
- }
725
- :global {
726
- .tiptap:focus-visible {
727
- outline: none;
728
- }
729
- .editor__content p {
730
- line-height: 1.5;
731
- }
732
- .editor__content ul {
733
- list-style-type: disc;
734
- margin-left: 1.5rem;
735
- }
736
- .editor__content ol {
737
- list-style-type: decimal;
738
- margin-left: 1.5rem;
739
- }
740
- .editor__content h1,
741
- .editor__content h2,
742
- .editor__content h3 {
743
- font-weight: 600;
744
- }
745
- .editor__content h1 {
746
- font-size: 2rem;
747
- }
748
- .editor__content h2 {
749
- font-size: 1.5rem;
750
- }
751
- .editor__content h3 {
752
- font-size: 1.25rem;
753
- }
754
- .editor__content a {
755
- color: var(--link-color);
756
- text-decoration: none;
757
- cursor: pointer;
758
- font-weight: var(--font-weight-6);
759
- }
760
- .editor__content a:hover {
761
- text-decoration: underline;
762
- }
763
- .editor__content strong {
764
- font-weight: 700;
765
- }
766
- .editor__content em {
767
- font-style: italic;
768
- }
769
- .editor__content blockquote {
770
- border-left: 4px solid var(--contrastMedium);
771
- color: var(--fgMuted);
772
- padding-left: 1rem;
773
- margin-left: 0;
774
- font-family: var(--font-sans);
775
- }
776
- .editor__content hr {
777
- height: 1px;
778
- background-color: var(--contrastMedium);
779
- }
780
- .editor__toolbarTextOptions {
781
- display: flex;
782
- flex-direction: column;
783
- gap: 0.5rem;
784
- }
785
- .editor__toolbarTextOptions button {
786
- cursor: pointer;
787
- padding: 0.25rem 0.5rem;
788
- width: 100%;
789
- }
790
- .editor__toolbarTextOptions button:hover {
791
- background-color: var(--contrastLow);
792
- }
793
-
794
- .editor__toolbarTextH1,
795
- .editor__toolbarTextH2,
796
- .editor__toolbarTextH3 {
797
- font-weight: 600;
798
- }
799
- .editor__toolbarTextP {
800
- font-weight: normal;
801
- }
802
- .editor__toolbarTextH1 {
803
- font-size: 2.5rem;
804
- }
805
- .editor__toolbarTextH2 {
806
- font-size: 2rem;
807
- }
808
- .editor__toolbarTextH3 {
809
- font-size: 1.5rem;
810
- }
811
- .editor__toolbarTrigger {
812
- display: flex;
813
- align-items: center;
814
- gap: 0.5rem;
815
- cursor: pointer;
816
- border: solid 2px transparent;
817
- padding: 0.25rem 0.5rem;
818
- border-radius: var(--radius-2);
819
- }
820
- .editor__toolbarTrigger:hover {
821
- background-color: var(--iconBtn-bgHover);
822
- border: var(--iconBtn-borderHover);
823
- }
824
- }
825
- </style>