@poirazis/supercomponents-shared 1.2.14 → 1.2.16

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.
@@ -1,869 +0,0 @@
1
- # Using JSDoc for Type Safety in SuperTableCells
2
-
3
- This guide demonstrates how to use JSDoc annotations for **type definitions only** (CellOptions, CellApi) - not for annotating every variable and function.
4
-
5
- ## Benefits of JSDoc
6
-
7
- 1. **No TypeScript compilation** - Pure JavaScript
8
- 2. **IntelliSense for important types** - Autocomplete for cellOptions and cellApi
9
- 3. **Simple and clean** - Only annotate what matters
10
- 4. **Better debugging** - Source code matches what runs
11
-
12
- ## What to Annotate
13
-
14
- ✅ **DO annotate:**
15
-
16
- - `cellOptions` prop - so you get autocomplete for options
17
- - `cellApi` export - so consumers know what methods are available
18
- - Component props (`value`, `formattedValue`, etc.)
19
-
20
- ❌ **DON'T annotate:**
21
-
22
- - Local variables (let timer, let editor, etc.)
23
- - Function parameters in FSM methods
24
- - Helper functions
25
- - Event handlers
26
-
27
- ## Type Definitions
28
-
29
- All types are defined in `types.js` using a **unified CellOptions type**:
30
-
31
- - `CellOptions` - Single type with common base properties + optional type-specific extensions
32
- - `CellApi` - Standard API interface exported by all cells
33
- - `CellRole`, `CellState`, `CellAlign` - Enum types
34
- - `FieldSchema` - Budibase field schema type
35
-
36
- ### CellOptions Structure
37
-
38
- The unified `CellOptions` type contains:
39
-
40
- - **Common properties** (role, readonly, disabled, placeholder, etc.) - used by all cells
41
- - **Type-specific properties** organized by category (Number, Boolean, Options, Datetime, etc.)
42
-
43
- All properties are optional except where noted. Components only use the properties relevant to them.
44
-
45
- ## Cell FSM State Machine
46
-
47
- All cell components use a finite state machine (FSM) managed by `svelte-fsm` to handle user interactions and data loading states. The FSM provides a clean, declarative way to manage component state transitions.
48
-
49
- ### Core States
50
-
51
- The cell FSM supports three core reachable states:
52
-
53
- | State | Usage | Transitions |
54
- | ----------- | --------------------------------------- | --------------------------------------- |
55
- | **View** | Default display state, read-only | → Editing (on focus) |
56
- | **Editing** | User is actively editing the cell value | → View (on focusout, submit, or cancel) |
57
- | **Loading** | Data is being fetched or processed | → View (when data loads) |
58
-
59
- ### State Diagram
60
-
61
- ```
62
- ┌──────────────────────────────────┐
63
- │ │
64
- │ View (Display Mode) │
65
- │ - Shows formatted value │
66
- │ - Read-only or ready to edit │
67
- │ │
68
- └──────────────────────────────────┘
69
-
70
- │ focusout()
71
- │ submit()
72
- │ cancel()
73
-
74
-
75
- ┌──────────────────────────────────┐
76
- │ │
77
- │ Editing (Edit Mode) │
78
- │ - Input field active │
79
- │ - User can modify value │
80
- │ - Dispatch change events │
81
- │ │
82
- └──────────────────────────────────┘
83
-
84
- │ focus()
85
- │ (when not readonly)
86
-
87
- ┌──────────────────────────────────┐
88
- │ │
89
- │ Loading (Async Mode) │
90
- │ - Fetching data from server │
91
- │ - Data-dependent cells only │
92
- │ - Auto-transitions to View │
93
- │ │
94
- └──────────────────────────────────┘
95
- ```
96
-
97
- ### Global Transitions (Available in All States)
98
-
99
- All states support these global methods via the `"*"` state:
100
-
101
- ```javascript
102
- export const cellState = fsm("View", {
103
- "*": {
104
- goTo(state) {
105
- // Manual state transition (for external control)
106
- return state;
107
- },
108
- reset(value) {
109
- // Reset to initial value and View state
110
- localValue = undefined;
111
- },
112
- },
113
- // ... state-specific transitions
114
- });
115
- ```
116
-
117
- ### Lifecycle Hooks
118
-
119
- State transitions can trigger lifecycle methods:
120
-
121
- - **`_enter()`** - Called when entering a state
122
- - **`_exit()`** - Called when exiting a state
123
-
124
- Example from CellString:
125
-
126
- ```javascript
127
- Editing: {
128
- _enter() {
129
- originalValue = JSON.stringify(localValue); // Save for rollback
130
- dispatch("enteredit"); // Notify parent
131
- },
132
- _exit() {
133
- dispatch("exitedit"); // Cleanup
134
- },
135
- focusout() {
136
- this.submit(); // Submit on blur
137
- },
138
- submit() {
139
- dispatch("change", localValue);
140
- return "View"; // Transition back to View
141
- },
142
- },
143
- ```
144
-
145
- ### Usage Pattern
146
-
147
- ```javascript
148
- // Declare FSM with initial state
149
- export const cellState = fsm(cellOptions.initialState ?? "View", {
150
- View: {
151
- /* ... */
152
- },
153
- Editing: {
154
- /* ... */
155
- },
156
- Loading: {
157
- /* ... */
158
- }, // Optional, for data-dependent cells
159
- });
160
-
161
- // Subscribe to state changes in component
162
- $: inEdit = $cellState === "Editing";
163
- $: isDirty = inEdit && originalValue !== JSON.stringify(localValue);
164
-
165
- // Trigger transitions from event handlers
166
- const handleFocus = () => cellState.focus();
167
- const handleBlur = () => cellState.focusout();
168
- ```
169
-
170
- ### Data-Dependent Cells
171
-
172
- Cells that fetch data (CellOptions, CellTags, CellOptionsAdvanced) use the Loading state:
173
-
174
- ```javascript
175
- Loading: {
176
- _enter() {
177
- // Fetch data from server
178
- loadOptions();
179
- },
180
- syncFetch(fetch) {
181
- // Handle synchronous fetch results
182
- return "View"; // Auto-transition when done
183
- },
184
- focus() {
185
- // Allow editing while loading
186
- return "Editing";
187
- },
188
- },
189
- ```
190
-
191
- ## JavaScript Example: CellString.svelte (Reference Implementation)
192
-
193
- ```svelte
194
- <script>
195
- import { createEventDispatcher, getContext, onMount, onDestroy } from "svelte";
196
- import fsm from "svelte-fsm";
197
-
198
- /**
199
- * @typedef {import('./types.js').CellOptions} CellOptions
200
- * @typedef {import('./types.js').CellApi} CellApi
201
- */
202
-
203
- const dispatch = createEventDispatcher();
204
- const { processStringSync } = getContext("sdk");
205
-
206
- /** @type {string | null} */
207
- export let value;
208
-
209
- /** @type {string | undefined} */
210
- export let formattedValue = undefined;
211
-
212
- /** @type {CellOptions} */
213
- export let cellOptions = {
214
- role: "formInput",
215
- initialState: "Editing",
216
- debounce: 250,
217
- };
218
-
219
- export let autofocus = false;
220
-
221
- // Local state - no annotations needed
222
- let timer;
223
- let originalValue;
224
- let editor;
225
- let lastEdit;
226
- let localValue = value;
227
- let state = (cellOptions?.initialState === "Loading" ? "View" : cellOptions?.initialState) ?? "View";
228
- let errors = [];
229
-
230
- // Reactive declarations
231
- $: error = cellOptions?.error || errors.length > 0;
232
- $: inEdit = $cellState === "Editing";
233
- $: isDirty = !!lastEdit && originalValue !== localValue;
234
-
235
- // FSM - no annotations needed for methods
236
- export const cellState = fsm(state ?? "View", {
237
- "*": {
238
- goTo(state) {
239
- return state;
240
- },
241
- reset(newValue) {
242
- if (newValue === localValue) return;
243
- localValue = value;
244
- lastEdit = undefined;
245
- originalValue = undefined;
246
- errors = [];
247
- return state;
248
- },
249
- },
250
- View: {
251
- _enter() {
252
- localValue = value;
253
- },
254
- focus() {
255
- if (!cellOptions.readonly && !cellOptions.disabled) {
256
- return "Editing";
257
- }
258
- },
259
- },
260
- Editing: {
261
- _enter() {
262
- originalValue = value;
263
- setTimeout(() => editor?.focus(), 50);
264
- dispatch("enteredit");
265
- },
266
- _exit() {
267
- originalValue = undefined;
268
- lastEdit = undefined;
269
- dispatch("exitedit");
270
- },
271
- focusout(e) {
272
- dispatch("focusout");
273
- this.submit();
274
- },
275
- submit() {
276
- if (isDirty) {
277
- dispatch("change", localValue);
278
- }
279
- return state;
280
- },
281
- cancel() {
282
- value = originalValue ?? null;
283
- dispatch("cancel");
284
- return state;
285
- },
286
- debounce(e) {
287
- const target = e.target;
288
- localValue = target.value;
289
- lastEdit = new Date();
290
- if (cellOptions?.debounce) {
291
- clearTimeout(timer);
292
- timer = setTimeout(() => {
293
- dispatch("change", localValue);
294
- }, cellOptions.debounce ?? 0);
295
- }
296
- },
297
- handleKeyboard(e) {
298
- if (e.key === "Enter" && !e.shiftKey) {
299
- this.submit();
300
- }
301
- if (e.key === "Escape") {
302
- this.cancel();
303
- }
304
- },
305
- },
306
- });
307
-
308
- // Annotate the public API
309
- /** @type {CellApi} */
310
- export const cellApi = {
311
- focus: () => cellState.focus(),
312
- reset: () => cellState.reset(value),
313
- isEditing: () => $cellState === "Editing",
314
- isDirty: () => isDirty,
315
- getValue: () => localValue,
316
- setError: (err) => {
317
- errors = [...errors, err];
318
- },
319
- clearError: () => {
320
- errors = [];
321
- },
322
- setValue: (val) => {
323
- localValue = val;
324
- },
325
- };
326
-
327
- // No annotations needed for helpers
328
- const focus = (node) => {
329
- node?.focus();
330
- };
331
-
332
- onMount(() => {
333
- if (autofocus) {
334
- setTimeout(() => cellState.focus(), 50);
335
- }
336
- });
337
-
338
- onDestroy(() => {
339
- if (timer) clearTimeout(timer);
340
- });
341
- </script>
342
- ```
343
-
344
- ```svelte
345
- <script>
346
- // @ts-check - Enable TypeScript checking in JavaScript
347
- import { createEventDispatcher, getContext, onMount, onDestroy } from "svelte";
348
- import fsm from "svelte-fsm";
349
-
350
- // Import types for IntelliSense (no runtime impact)
351
- /**
352
- * @typedef {import('./types.js').CellStringOptions} CellStringOptions
353
- * @typedef {import('./types.js').CellApi} CellApi
354
- */
355
-
356
- const { processStringSync } = getContext("sdk");
357
- const dispatch = createEventDispatcher();
358
-
359
- // Typed props with JSDoc
360
- /** @type {string | null} */
361
- export let value;
362
-
363
- /** @type {string | undefined} */
364
- export let formattedValue = undefined;
365
-
366
- /** @type {CellStringOptions} */
367
- export let cellOptions = {
368
- role: "formInput",
369
- initialState: "Editing",
370
- debounce: 250,
371
- };
372
-
373
- /** @type {boolean} */
374
- export let autofocus = false;
375
-
376
- // Typed local variables
377
- /** @type {ReturnType<typeof setTimeout> | undefined} */
378
- let timer;
379
-
380
- /** @type {string | null | undefined} */
381
- let originalValue;
382
-
383
- /** @type {HTMLInputElement | HTMLTextAreaElement | undefined} */
384
- let editor;
385
-
386
- /** @type {Date | undefined} */
387
- let lastEdit;
388
-
389
- /** @type {string | null} */
390
- let localValue = value;
391
-
392
- /** @type {"View" | "Editing"} */
393
- let state = (cellOptions?.initialState === "Loading" ? "View" : cellOptions?.initialState) ?? "View";
394
-
395
- /** @type {string[]} */
396
- let errors = [];
397
-
398
- // Reactive declarations
399
- $: error = cellOptions?.error || errors.length > 0;
400
- $: icon = error ? "ph ph-warning" : cellOptions?.icon;
401
- $: inEdit = $cellState === "Editing";
402
- $: isDirty = !!lastEdit && originalValue !== localValue;
403
-
404
- // FSM with typed transitions
405
- export const cellState = fsm(state ?? "View", {
406
- "*": {
407
- /** @param {string} state */
408
- goTo(state) {
409
- return state;
410
- },
411
- /** @param {string | null} newValue */
412
- reset(newValue) {
413
- if (newValue === localValue) return;
414
- localValue = value;
415
- lastEdit = undefined;
416
- originalValue = undefined;
417
- errors = [];
418
- return state;
419
- },
420
- },
421
- View: {
422
- _enter() {
423
- localValue = value;
424
- },
425
- focus() {
426
- if (!cellOptions.readonly && !cellOptions.disabled) {
427
- return "Editing";
428
- }
429
- },
430
- },
431
- Editing: {
432
- _enter() {
433
- originalValue = value;
434
- setTimeout(() => editor?.focus(), 50);
435
- dispatch("enteredit");
436
- },
437
- _exit() {
438
- originalValue = undefined;
439
- lastEdit = undefined;
440
- dispatch("exitedit");
441
- },
442
- /** @param {FocusEvent} e */
443
- focusout(e) {
444
- dispatch("focusout");
445
- this.submit();
446
- },
447
- submit() {
448
- if (isDirty) {
449
- dispatch("change", localValue);
450
- }
451
- return state;
452
- },
453
- cancel() {
454
- value = originalValue ?? null;
455
- dispatch("cancel");
456
- return state;
457
- },
458
- /** @param {Event} e */
459
- debounce(e) {
460
- const target = /** @type {HTMLInputElement | HTMLTextAreaElement} */ (e.target);
461
- localValue = target.value;
462
- lastEdit = new Date();
463
- if (cellOptions?.debounce) {
464
- clearTimeout(timer);
465
- timer = setTimeout(() => {
466
- dispatch("change", localValue);
467
- }, cellOptions.debounce ?? 0);
468
- }
469
- },
470
- /** @param {KeyboardEvent} e */
471
- handleKeyboard(e) {
472
- if (e.key === "Enter" && !e.shiftKey) {
473
- this.submit();
474
- }
475
- if (e.key === "Escape") {
476
- this.cancel();
477
- }
478
- },
479
- },
480
- });
481
-
482
- // Typed API object
483
- /** @type {CellApi} */
484
- export const cellApi = {
485
- focus: () => cellState.focus(),
486
- reset: () => cellState.reset(value),
487
- isEditing: () => $cellState === "Editing",
488
- isDirty: () => isDirty,
489
- getValue: () => localValue,
490
- /** @param {string} err */
491
- setError: (err) => {
492
- errors = [...errors, err];
493
- },
494
- clearError: () => {
495
- errors = [];
496
- },
497
- /** @param {string | null} val */
498
- setValue: (val) => {
499
- localValue = val;
500
- },
501
- };
502
-
503
- // Lifecycle
504
- onMount(() => {
505
- if (autofocus) {
506
- setTimeout(() => cellState.focus(), 50);
507
- }
508
- });
509
-
510
- onDestroy(() => {
511
- if (timer) clearTimeout(timer);
512
- });
513
- </script>
514
- ```
515
-
516
- ```svelte
517
- <script>
518
- import { createEventDispatcher, getContext, onMount, onDestroy } from "svelte";
519
- import fsm from "svelte-fsm";
520
-
521
- // Import types for IntelliSense (no runtime impact)
522
- /**
523
- * @typedef {import('./types.js').CellBooleanOptions} CellBooleanOptions
524
- * @typedef {import('./types.js').CellApi<boolean>} CellBooleanApi
525
- * @typedef {import('./types.js').CellEvents<boolean>} CellBooleanEvents
526
- */
527
-
528
- const { processStringSync } = getContext("sdk");
529
- const dispatch = createEventDispatcher();
530
-
531
- // Typed props with JSDoc
532
- /** @type {boolean | null} */
533
- export let value;
534
-
535
- /** @type {string | undefined} */
536
- export let formattedValue;
537
-
538
- /** @type {CellBooleanOptions} */
539
- export let cellOptions;
540
-
541
- /** @type {boolean} */
542
- export let autofocus;
543
-
544
- // Typed local variables
545
- /** @type {boolean | null} */
546
- let originalValue = value;
547
-
548
- /** @type {boolean} */
549
- let localValue = value ?? false;
550
-
551
- // FSM with typed states
552
- export let cellState = fsm(cellOptions.initialState ?? "View", {
553
- "*": {
554
- /** @param {string} state */
555
- goTo(state) {
556
- return state;
557
- },
558
- },
559
- View: {
560
- _enter() {
561
- localValue = value ?? false;
562
- originalValue = value;
563
- },
564
- /** @param {boolean | null} val */
565
- reset(val) {
566
- localValue = val ?? false;
567
- originalValue = val;
568
- return cellOptions.initialState ?? "View";
569
- },
570
- focus() {
571
- if (!cellOptions.readonly && !cellOptions.disabled) return "Editing";
572
- },
573
- },
574
- Editing: {
575
- _enter() {
576
- dispatch("enteredit");
577
- },
578
- _exit() {
579
- dispatch("exitedit");
580
- },
581
- submit() {
582
- value = localValue;
583
- dispatch("change", localValue);
584
- return "View";
585
- },
586
- cancel() {
587
- localValue = originalValue ?? false;
588
- dispatch("cancel");
589
- return "View";
590
- },
591
- },
592
- });
593
-
594
- // Typed API object
595
- /** @type {CellBooleanApi} */
596
- export const cellApi = {
597
- focus: () => cellState.focus(),
598
- reset: () => cellState.reset(value),
599
- isEditing: () => cellState.current === "Editing",
600
- isDirty: () => localValue !== originalValue,
601
- getValue: () => localValue,
602
- /** @param {string} err */
603
- setError: (err) => {},
604
- clearError: () => {},
605
- /** @param {boolean} val */
606
- setValue: (val) => {
607
- localValue = val;
608
- },
609
- };
610
-
611
- // Typed event handlers
612
- /** @param {Event} e */
613
- const handleClick = (e) => {
614
- if (cellState.current === "View") {
615
- cellState.focus();
616
- }
617
- };
618
-
619
- /** @param {KeyboardEvent} e */
620
- const handleKeyboard = (e) => {
621
- if (e.key === "Escape") {
622
- cellState.cancel();
623
- } else if (e.key === "Enter") {
624
- cellState.submit();
625
- } else if (e.key === " ") {
626
- e.preventDefault();
627
- localValue = !localValue;
628
- }
629
- };
630
- </script>
631
- ```
632
-
633
- ## TypeScript Example: CellString.svelte
634
-
635
- **Note:** We recommend using JSDoc instead of TypeScript for simplicity. This example is shown for comparison only.
636
-
637
- ```svelte
638
- <script lang="ts">
639
- import { createEventDispatcher, getContext, onMount, onDestroy } from "svelte";
640
- import fsm from "svelte-fsm";
641
- import type { CellStringOptions, CellApi, CellEvents } from "./types";
642
-
643
- const { processStringSync } = getContext("sdk");
644
- const dispatch = createEventDispatcher<CellEvents<string | null>>();
645
-
646
- export let value: string | null;
647
- export let formattedValue: string | undefined;
648
- export let cellOptions: CellStringOptions;
649
- export let autofocus: boolean;
650
-
651
- let originalValue: string | null = value;
652
- let localValue: string = value ?? "";
653
- let editor: HTMLInputElement | HTMLTextAreaElement;
654
-
655
- export let cellState = fsm(cellOptions.initialState ?? "View", {
656
- "*": {
657
- goTo(state: string) {
658
- return state;
659
- },
660
- },
661
- View: {
662
- _enter() {
663
- localValue = value ?? "";
664
- originalValue = value;
665
- },
666
- reset(val: string | null) {
667
- localValue = val ?? "";
668
- originalValue = val;
669
- return cellOptions.initialState ?? "View";
670
- },
671
- focus() {
672
- if (!cellOptions.readonly && !cellOptions.disabled) return "Editing";
673
- },
674
- },
675
- Editing: {
676
- _enter() {
677
- dispatch("enteredit");
678
- editor?.focus();
679
- },
680
- _exit() {
681
- dispatch("exitedit");
682
- },
683
- submit() {
684
- value = localValue || null;
685
- dispatch("change", value);
686
- return "View";
687
- },
688
- cancel() {
689
- localValue = originalValue ?? "";
690
- dispatch("cancel");
691
- return "View";
692
- },
693
- },
694
- });
695
-
696
- export const cellApi: CellApi<string> = {
697
- focus: () => cellState.focus(),
698
- reset: () => cellState.reset(value),
699
- isEditing: () => cellState.current === "Editing",
700
- isDirty: () => localValue !== originalValue,
701
- getValue: () => localValue,
702
- setError: (err: string) => {},
703
- clearError: () => {},
704
- setValue: (val: string) => {
705
- localValue = val;
706
- },
707
- };
708
- </script>
709
- ```
710
-
711
- ## Usage in Parent Components
712
-
713
- ### JavaScript Parent
714
-
715
- ```svelte
716
- <script>
717
- import { CellString } from "@poirazis/supercomponents-shared";
718
-
719
- /**
720
- * @typedef {import('@poirazis/supercomponents-shared').CellStringOptions} CellStringOptions
721
- * @typedef {import('@poirazis/supercomponents-shared').CellApi<string>} CellStringApi
722
- */
723
-
724
- /** @type {string | null} */
725
- let value = "Hello";
726
-
727
- /** @type {CellStringOptions} */
728
- const options = {
729
- role: "tableCell",
730
- placeholder: "Enter text...",
731
- debounce: 300,
732
- align: "flex-start",
733
- };
734
-
735
- /** @type {CellStringApi} */
736
- let api;
737
-
738
- function handleChange(event) {
739
- console.log("Value changed:", event.detail);
740
- }
741
-
742
- function focusCell() {
743
- api?.focus();
744
- }
745
- </script>
746
-
747
- <CellString
748
- bind:value
749
- bind:cellApi={api}
750
- cellOptions={options}
751
- on:change={handleChange}
752
- />
753
- ```
754
-
755
- ### TypeScript Parent
756
-
757
- ```svelte
758
- <script lang="ts">
759
- import { CellString, type CellStringOptions, type CellApi } from "@poirazis/supercomponents-shared";
760
-
761
- let value: string | null = "Hello";
762
-
763
- const options: CellStringOptions = {
764
- role: "tableCell",
765
- placeholder: "Enter text...",
766
- debounce: 300,
767
- align: "flex-start",
768
- };
769
-
770
- let api: CellApi<string>;
771
-
772
- function handleChange(event: CustomEvent<string | null>) {
773
- console.log("Value changed:", event.detail);
774
- }
775
-
776
- function focusCell() {
777
- api?.focus();
778
- }
779
- </script>
780
-
781
- <CellString
782
- bind:value
783
- bind:cellApi={api}
784
- cellOptions={options}
785
- on:change={handleChange}
786
- />
787
- ```
788
-
789
- ## Common Patterns
790
-
791
- ### Minimal JSDoc - Just the Essentials
792
-
793
- ````javascript
794
- /**
795
- * @typedef {import('./types.js').CellNumberOptions} CellNumberOptions
796
- * @typedef {import('./types.js').CellApi} CellApi
797
- */
798
-
799
- /** @type {number | null} */
800
- export let value;
801
-
802
- /** @type {CellNumberOptions} */
803
- export let cellOptions = {};
804
-
805
- // Everything else - no annotations
806
- let localValue = value;
807
- let editor;
808
-
809
- export const cellState = fsm("View", {
810
- View: {
811
- focus() {
812
- return "Editing";
813
- }
814
- }
815
- });
816
-
817
- /** @type {CellApi} */
818
- export const cellApi = {
819
- focus: () => cellState.focus(),
820
- getValue: () => localValue,
821
- // ... other methods
822
- };
823
- ## Type Enforcement
824
-
825
- ### VS Code Settings
826
-
827
- Add to `.vscode/settings.json`:
828
-
829
- ```json
830
- {
831
- "js/ts.implicitProjectConfig.checkJs": true,
832
- "javascript.validate.enable": true,
833
- "typescript.tsdk": "node_modules/typescript/lib"
834
- }
835
- ````
836
-
837
- ### Component-Level Enforcement
838
-
839
- Add to top of JavaScript files:
840
-
841
- ```javascript
842
- // @ts-check
843
- ```
844
-
845
- This enables TypeScript checking in JavaScript files.
846
-
847
- ## Migration Strategy
848
-
849
- 1. **Add typedef imports** at top of script
850
- 2. **Annotate cellOptions prop** with component-specific options type
851
- 3. **Annotate cellApi export** with CellApi type
852
- 4. **Annotate main props** (value, formattedValue)
853
- 5. **Done!** - Don't annotate anything else
854
-
855
- ## IntelliSense Benefits
856
-
857
- With just these 3-4 JSDoc annotations, you get:
858
-
859
- - **Autocomplete for cellOptions** - VS Code shows all available options
860
- - **cellApi IntelliSense** - Consumers see all available methods
861
- - **Prop type checking** - Errors if wrong types are passed
862
- - **Clean code** - No clutter from over-annotation
863
-
864
- ## Best Practices
865
-
866
- 1. **Only annotate public interfaces** - cellOptions and cellApi
867
- 2. **Keep it minimal** - Less is more
868
- 3. **No @ts-check needed** - Too strict, unnecessary noise
869
- 4. **Trust JavaScript** - Let Svelte handle the rest