@r2digisolutions/components 0.1.5 → 0.1.7

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 (72) hide show
  1. package/README.md +6 -1
  2. package/dist/components/atoms/Checkbox/Checkbox.svelte +10 -3
  3. package/dist/components/atoms/Checkbox/Checkbox.svelte.d.ts +3 -0
  4. package/dist/components/atoms/Input/Input.svelte +17 -2
  5. package/dist/components/atoms/Input/Input.svelte.d.ts +6 -1
  6. package/dist/components/atoms/MarqueeRect/MarqueeRect.svelte +21 -0
  7. package/dist/components/atoms/MarqueeRect/MarqueeRect.svelte.d.ts +8 -0
  8. package/dist/components/atoms/Textarea/Textarea.svelte +4 -1
  9. package/dist/components/atoms/Textarea/Textarea.svelte.d.ts +1 -0
  10. package/dist/components/atoms/Toggle/Toggle.svelte +1 -1
  11. package/dist/components/molecules/BulkActionBar/BulkActionBar.svelte +19 -5
  12. package/dist/components/molecules/CodeEditor/CodeEditor.svelte +13 -10
  13. package/dist/components/molecules/ColorPicker/ColorPicker.svelte +166 -96
  14. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanel.stories.d.ts +9 -0
  15. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanel.stories.js +8 -0
  16. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanel.svelte +412 -0
  17. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanel.svelte.d.ts +14 -0
  18. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanelStory.svelte +27 -0
  19. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanelStory.svelte.d.ts +3 -0
  20. package/dist/components/molecules/ContextMenu/ContextMenu.svelte +73 -21
  21. package/dist/components/molecules/ContextMenu/ContextMenu.svelte.d.ts +8 -1
  22. package/dist/components/molecules/DateTimePicker/DateTimePicker.svelte.d.ts +1 -1
  23. package/dist/components/molecules/FormDateTimePicker/FormDateTimePicker.svelte.d.ts +1 -1
  24. package/dist/components/molecules/Gauge/Gauge.stories.d.ts +1 -1
  25. package/dist/components/molecules/InfiniteScroll/InfiniteScroll.stories.d.ts +1 -1
  26. package/dist/components/organisms/AssetPickerPage/AssetPickerPage.svelte.d.ts +1 -1
  27. package/dist/components/organisms/BacklogPage/BacklogPage.svelte.d.ts +1 -1
  28. package/dist/components/organisms/CalendarApp/CalendarApp.svelte.d.ts +1 -1
  29. package/dist/components/organisms/DataGrid/DataGrid.stories.d.ts +65 -0
  30. package/dist/components/organisms/DataGrid/DataGrid.stories.js +95 -0
  31. package/dist/components/organisms/DataGrid/DataGrid.svelte +2264 -0
  32. package/dist/components/organisms/DataGrid/DataGrid.svelte.d.ts +99 -0
  33. package/dist/components/organisms/DataGrid/DataGridStory.svelte +659 -0
  34. package/dist/components/organisms/DataGrid/DataGridStory.svelte.d.ts +11 -0
  35. package/dist/components/organisms/DataGrid/breakpoints.d.ts +10 -0
  36. package/dist/components/organisms/DataGrid/breakpoints.js +20 -0
  37. package/dist/components/organisms/DataGrid/cellEditors.d.ts +8 -0
  38. package/dist/components/organisms/DataGrid/cellEditors.js +59 -0
  39. package/dist/components/organisms/DataGrid/conditionalFormat.d.ts +11 -0
  40. package/dist/components/organisms/DataGrid/conditionalFormat.js +98 -0
  41. package/dist/components/organisms/DataGrid/filterRows.d.ts +5 -0
  42. package/dist/components/organisms/DataGrid/filterRows.js +60 -0
  43. package/dist/components/organisms/DataGrid/marks.d.ts +9 -0
  44. package/dist/components/organisms/DataGrid/marks.js +91 -0
  45. package/dist/components/organisms/DataGrid/types.d.ts +126 -0
  46. package/dist/components/organisms/DataGrid/types.js +21 -0
  47. package/dist/components/organisms/ResourceStudio/ResourceStudio.stories.d.ts +33 -0
  48. package/dist/components/organisms/ResourceStudio/ResourceStudio.stories.js +56 -0
  49. package/dist/components/organisms/ResourceStudio/ResourceStudio.svelte +830 -0
  50. package/dist/components/organisms/ResourceStudio/ResourceStudio.svelte.d.ts +68 -0
  51. package/dist/components/organisms/ResourceStudio/ResourceStudioStory.svelte +600 -0
  52. package/dist/components/organisms/ResourceStudio/ResourceStudioStory.svelte.d.ts +7 -0
  53. package/dist/components/organisms/ResourceStudio/fieldsToColumns.d.ts +4 -0
  54. package/dist/components/organisms/ResourceStudio/fieldsToColumns.js +20 -0
  55. package/dist/components/organisms/ResourceStudio/fieldsToSchema.d.ts +6 -0
  56. package/dist/components/organisms/ResourceStudio/fieldsToSchema.js +53 -0
  57. package/dist/components/organisms/ResourceStudio/types.d.ts +69 -0
  58. package/dist/components/organisms/ResourceStudio/types.js +15 -0
  59. package/dist/components/organisms/Scheduler/Scheduler.svelte.d.ts +1 -1
  60. package/dist/components/organisms/SchemaForm/SchemaForm.svelte +10 -3
  61. package/dist/components/organisms/SchemaForm/SchemaForm.svelte.d.ts +1 -0
  62. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplate.stories.d.ts +25 -0
  63. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplate.stories.js +18 -0
  64. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplate.svelte +192 -0
  65. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplate.svelte.d.ts +62 -0
  66. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplateStory.svelte +129 -0
  67. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplateStory.svelte.d.ts +7 -0
  68. package/dist/index.d.ts +11 -0
  69. package/dist/index.js +8 -0
  70. package/dist/utils/marqueeSelect.d.ts +48 -0
  71. package/dist/utils/marqueeSelect.js +189 -0
  72. package/package.json +1 -1
@@ -0,0 +1,129 @@
1
+ <script lang="ts">
2
+ import ResourceStudioTemplate from './ResourceStudioTemplate.svelte';
3
+ import type { ResourceDefinition } from '../../organisms/ResourceStudio/types.js';
4
+ import Badge from '../../atoms/Badge/Badge.svelte';
5
+ import Button from '../../atoms/Button/Button.svelte';
6
+ import CodeEditor from '../../molecules/CodeEditor/CodeEditor.svelte';
7
+
8
+ type Example = 'crud' | 'sql' | 'mainOnly';
9
+
10
+ let { example = 'crud' }: { example?: Example } = $props();
11
+
12
+ const resources = $derived.by((): ResourceDefinition[] => {
13
+ const sql = example === 'sql';
14
+ return [
15
+ {
16
+ id: 'users',
17
+ name: sql ? 'users' : 'Users',
18
+ group: sql ? 'public' : 'Auth',
19
+ recordCount: 3,
20
+ fields: [
21
+ { id: 'id', label: 'ID', type: 'string', readonly: true },
22
+ { id: 'name', label: 'Name', type: 'string', required: true },
23
+ { id: 'email', label: 'Email', type: 'string', required: true },
24
+ {
25
+ id: 'role',
26
+ label: 'Role',
27
+ type: 'enum',
28
+ options: [
29
+ { value: 'admin', label: 'Admin' },
30
+ { value: 'editor', label: 'Editor' }
31
+ ]
32
+ }
33
+ ]
34
+ },
35
+ {
36
+ id: 'orders',
37
+ name: sql ? 'orders' : 'Orders',
38
+ group: sql ? 'public' : 'Commerce',
39
+ recordCount: 3,
40
+ fields: [
41
+ { id: 'id', label: 'ID', type: 'string', readonly: true },
42
+ {
43
+ id: 'status',
44
+ label: 'Status',
45
+ type: 'enum',
46
+ options: [
47
+ { value: 'paid', label: 'Paid' },
48
+ { value: 'pending', label: 'Pending' }
49
+ ]
50
+ },
51
+ { id: 'total', label: 'Total', type: 'number' }
52
+ ]
53
+ }
54
+ ];
55
+ });
56
+
57
+ let activeResourceId = $state('users');
58
+ let records = $state<Record<string, unknown>[]>([
59
+ { id: '1', name: 'Ana', email: 'ana@r2.dev', role: 'admin' },
60
+ { id: '2', name: 'Carlos', email: 'carlos@r2.dev', role: 'editor' },
61
+ { id: '3', name: 'Maya', email: 'maya@r2.dev', role: 'editor' }
62
+ ]);
63
+ let orderRecords = $state<Record<string, unknown>[]>([
64
+ { id: 'o1', status: 'paid', total: 120 },
65
+ { id: 'o2', status: 'pending', total: 80 },
66
+ { id: 'o3', status: 'paid', total: 440 }
67
+ ]);
68
+ let sqlText = $state('select * from users;');
69
+
70
+ $effect(() => {
71
+ // Only seed when the Storybook example changes.
72
+ activeResourceId = example === 'mainOnly' ? 'orders' : 'users';
73
+ });
74
+
75
+ const activeRecords = $derived(
76
+ activeResourceId === 'orders' ? orderRecords : records
77
+ );
78
+ </script>
79
+
80
+ <ResourceStudioTemplate
81
+ title={example === 'sql' ? 'Database Studio' : 'Admin'}
82
+ description={example === 'sql'
83
+ ? 'Drizzle-like browse UI built on ResourceStudio.'
84
+ : 'Full-page CRUD shell for your collections.'}
85
+ {resources}
86
+ bind:activeResourceId
87
+ records={activeRecords}
88
+ totalRecords={activeRecords.length}
89
+ layout={example === 'mainOnly' ? 'main-only' : 'sidebar-main-detail'}
90
+ modes={example === 'sql' ? ['browse', 'schema', 'query', 'detail'] : ['browse', 'detail', 'schema']}
91
+ labels={example === 'sql'
92
+ ? { resources: 'Tables', records: 'rows', addRecord: 'Insert row', query: 'SQL' }
93
+ : undefined}
94
+ oninsert={(row) => {
95
+ if (activeResourceId === 'orders') orderRecords = [row, ...orderRecords];
96
+ else records = [row, ...records];
97
+ }}
98
+ onupdate={(id, patch) => {
99
+ const apply = (list: Record<string, unknown>[]) =>
100
+ list.map((r) => (String(r.id) === id ? { ...r, ...patch } : r));
101
+ if (activeResourceId === 'orders') orderRecords = apply(orderRecords);
102
+ else records = apply(records);
103
+ }}
104
+ ondelete={(ids) => {
105
+ const filter = (list: Record<string, unknown>[]) =>
106
+ list.filter((r) => !ids.includes(String(r.id)));
107
+ if (activeResourceId === 'orders') orderRecords = filter(orderRecords);
108
+ else records = filter(records);
109
+ }}
110
+ >
111
+ {#snippet headerActions()}
112
+ <Button size="sm" variant="secondary">Docs</Button>
113
+ {/snippet}
114
+
115
+ {#snippet queryPanel()}
116
+ <div class="space-y-2">
117
+ <CodeEditor bind:value={sqlText} class="min-h-28" />
118
+ <Button size="sm" variant="primary">Run SQL</Button>
119
+ </div>
120
+ {/snippet}
121
+
122
+ {#snippet cell(row, column)}
123
+ {#if column.id === 'status' || column.id === 'role'}
124
+ <Badge variant="secondary">{String(row[column.id] ?? '')}</Badge>
125
+ {:else}
126
+ {row[column.id] == null ? '' : String(row[column.id])}
127
+ {/if}
128
+ {/snippet}
129
+ </ResourceStudioTemplate>
@@ -0,0 +1,7 @@
1
+ type Example = 'crud' | 'sql' | 'mainOnly';
2
+ type $$ComponentProps = {
3
+ example?: Example;
4
+ };
5
+ declare const ResourceStudioTemplateStory: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type ResourceStudioTemplateStory = ReturnType<typeof ResourceStudioTemplateStory>;
7
+ export default ResourceStudioTemplateStory;
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export { default as Avatar } from './components/atoms/Avatar/Avatar.svelte';
10
10
  export { default as Spinner } from './components/atoms/Spinner/Spinner.svelte';
11
11
  export { default as Toggle } from './components/atoms/Toggle/Toggle.svelte';
12
12
  export { default as Checkbox } from './components/atoms/Checkbox/Checkbox.svelte';
13
+ export { default as MarqueeRect } from './components/atoms/MarqueeRect/MarqueeRect.svelte';
13
14
  export { default as Chip } from './components/atoms/Chip/Chip.svelte';
14
15
  export { default as Tooltip } from './components/atoms/Tooltip/Tooltip.svelte';
15
16
  export { default as Divider } from './components/atoms/Divider/Divider.svelte';
@@ -433,6 +434,7 @@ export { default as CreditCardInput } from './components/molecules/CreditCardInp
433
434
  export type { CardBrand } from './components/molecules/CreditCardInput/CreditCardInput.svelte';
434
435
  export { default as BulkActionBar } from './components/molecules/BulkActionBar/BulkActionBar.svelte';
435
436
  export type { BulkAction, BulkActionVariant, BulkActionBarPlacement, BulkActionBarSize, BulkActionBarTone } from './components/molecules/BulkActionBar/BulkActionBar.svelte';
437
+ export { default as ConditionalFormatPanel } from './components/molecules/ConditionalFormatPanel/ConditionalFormatPanel.svelte';
436
438
  export { default as ReactionBar } from './components/molecules/ReactionBar/ReactionBar.svelte';
437
439
  export type { ReactionItem } from './components/molecules/ReactionBar/ReactionBar.svelte';
438
440
  export { default as AddressFields } from './components/molecules/AddressFields/AddressFields.svelte';
@@ -577,6 +579,12 @@ export type { SheetSize, SheetAccent } from './components/organisms/Sheet/Sheet.
577
579
  export { SHEET_SIZE_SNAPS } from './components/organisms/Sheet/Sheet.svelte';
578
580
  export { default as DataTable } from './components/organisms/DataTable/DataTable.svelte';
579
581
  export type { DataTableColumn } from './components/organisms/DataTable/DataTable.svelte';
582
+ export { default as DataGrid } from './components/organisms/DataGrid/DataGrid.svelte';
583
+ export type { DataGridColumn, DataGridEditType, DataGridEditConfig, DataGridEditOption, DataGridViewMode, DataGridFilter, Breakpoint, CellRef, GridSelection, CellNote, GridMark, MarkColor, MarkPreset, ConditionalFormatRule, ConditionalFormatStyle, FormatCondition, FormatTarget, SortDir } from './components/organisms/DataGrid/DataGrid.svelte';
584
+ export { EMPTY_SELECTION, noteKey, markKey, selectionCount, selectionLabel, evaluateFormatRules, evaluateCondition, styleToCss, inferCellEditor, resolveCellEditor, coerceEditValue, draftFromValue, MARK_COLORS, MARK_TINT, MARK_SWATCH, markTint, markSwatch, isMarkPreset, BREAKPOINT_MIN, matchesMinBreakpoint, isColumnVisibleAtWidth, filterRows, cellText, uniqueColumnValues } from './components/organisms/DataGrid/DataGrid.svelte';
585
+ export { default as ResourceStudio } from './components/organisms/ResourceStudio/ResourceStudio.svelte';
586
+ export type { FieldType, ResourceField, ResourceCapabilities, ResourceDefinition, ResourceStudioMode, ResourceStudioLayout, ResourceStudioLabels, ResourceStudioView } from './components/organisms/ResourceStudio/ResourceStudio.svelte';
587
+ export { DEFAULT_CAPABILITIES, resolveCapabilities, fieldsToColumns, fieldsToSchema } from './components/organisms/ResourceStudio/ResourceStudio.svelte';
580
588
  export { default as GanttChart } from './components/organisms/GanttChart/GanttChart.svelte';
581
589
  export type { GanttTask } from './components/organisms/GanttChart/GanttChart.svelte';
582
590
  export { default as ToastContainer } from './components/organisms/Toast/ToastContainer.svelte';
@@ -731,6 +739,7 @@ export { default as DashboardTemplate } from './components/templates/DashboardTe
731
739
  export type { DashboardTemplateDensity } from './components/templates/DashboardTemplate/DashboardTemplate.svelte';
732
740
  export { default as MarketingTemplate } from './components/templates/MarketingTemplate/MarketingTemplate.svelte';
733
741
  export { default as ListingTemplate } from './components/templates/ListingTemplate/ListingTemplate.svelte';
742
+ export { default as ResourceStudioTemplate } from './components/templates/ResourceStudioTemplate/ResourceStudioTemplate.svelte';
734
743
  export { default as CheckoutTemplate } from './components/templates/CheckoutTemplate/CheckoutTemplate.svelte';
735
744
  export type { CheckoutLine, CheckoutStep, CheckoutShippingOption } from './components/templates/CheckoutTemplate/CheckoutTemplate.svelte';
736
745
  export { default as DesktopTemplate } from './components/templates/DesktopTemplate/DesktopTemplate.svelte';
@@ -741,6 +750,8 @@ export type { VisibilityState } from './utils/pageVisibility.svelte.js';
741
750
  export { network } from './utils/network.svelte.js';
742
751
  export { DEFAULT_COLS, DEFAULT_ROW_HEIGHT, DEFAULT_GAP, GRID_DENSITY, clampItem, rectsOverlap, findCollisions, resolveCollisions, compactLayout, updateItem, addItem, removeItem, serializeLayout, parseLayout, layoutBounds, rescaleLayout, resizeItemByEdge } from './utils/layoutGrid.js';
743
752
  export type { GridItem, LayoutGridOptions, GridDensity, GridResizeEdge } from './utils/layoutGrid.js';
753
+ export { attachMarqueeSelect, collectMarqueeItems, defaultMarqueeIgnore, getMarqueeModifier, hitTestIds, normalizeRect, rectsIntersect, resolveMarqueeSelection } from './utils/marqueeSelect.js';
754
+ export type { AttachMarqueeSelectOptions, MarqueeDragState, MarqueeItemRect, MarqueeModifier, MarqueeRect as MarqueeRectBox } from './utils/marqueeSelect.js';
744
755
  export { encodeQR, qrToSvgPath, punchLogoHole } from './utils/qr.js';
745
756
  export { renderMarkdown, renderInline, escapeHtml, splitTableRow, slugifyHeading } from './utils/markdown.js';
746
757
  export { diffLines, diffWords, diffStats, toSplitRows } from './utils/diff.js';
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ export { default as Avatar } from './components/atoms/Avatar/Avatar.svelte';
14
14
  export { default as Spinner } from './components/atoms/Spinner/Spinner.svelte';
15
15
  export { default as Toggle } from './components/atoms/Toggle/Toggle.svelte';
16
16
  export { default as Checkbox } from './components/atoms/Checkbox/Checkbox.svelte';
17
+ export { default as MarqueeRect } from './components/atoms/MarqueeRect/MarqueeRect.svelte';
17
18
  export { default as Chip } from './components/atoms/Chip/Chip.svelte';
18
19
  export { default as Tooltip } from './components/atoms/Tooltip/Tooltip.svelte';
19
20
  export { default as Divider } from './components/atoms/Divider/Divider.svelte';
@@ -309,6 +310,7 @@ export { default as RadarChart } from './components/molecules/RadarChart/RadarCh
309
310
  export { default as InputGroup } from './components/molecules/InputGroup/InputGroup.svelte';
310
311
  export { default as CreditCardInput } from './components/molecules/CreditCardInput/CreditCardInput.svelte';
311
312
  export { default as BulkActionBar } from './components/molecules/BulkActionBar/BulkActionBar.svelte';
313
+ export { default as ConditionalFormatPanel } from './components/molecules/ConditionalFormatPanel/ConditionalFormatPanel.svelte';
312
314
  export { default as ReactionBar } from './components/molecules/ReactionBar/ReactionBar.svelte';
313
315
  export { default as AddressFields } from './components/molecules/AddressFields/AddressFields.svelte';
314
316
  export { default as RichTextEditor } from './components/molecules/RichTextEditor/RichTextEditor.svelte';
@@ -397,6 +399,10 @@ export { default as Drawer } from './components/organisms/Drawer/Drawer.svelte';
397
399
  export { default as Sheet } from './components/organisms/Sheet/Sheet.svelte';
398
400
  export { SHEET_SIZE_SNAPS } from './components/organisms/Sheet/Sheet.svelte';
399
401
  export { default as DataTable } from './components/organisms/DataTable/DataTable.svelte';
402
+ export { default as DataGrid } from './components/organisms/DataGrid/DataGrid.svelte';
403
+ export { EMPTY_SELECTION, noteKey, markKey, selectionCount, selectionLabel, evaluateFormatRules, evaluateCondition, styleToCss, inferCellEditor, resolveCellEditor, coerceEditValue, draftFromValue, MARK_COLORS, MARK_TINT, MARK_SWATCH, markTint, markSwatch, isMarkPreset, BREAKPOINT_MIN, matchesMinBreakpoint, isColumnVisibleAtWidth, filterRows, cellText, uniqueColumnValues } from './components/organisms/DataGrid/DataGrid.svelte';
404
+ export { default as ResourceStudio } from './components/organisms/ResourceStudio/ResourceStudio.svelte';
405
+ export { DEFAULT_CAPABILITIES, resolveCapabilities, fieldsToColumns, fieldsToSchema } from './components/organisms/ResourceStudio/ResourceStudio.svelte';
400
406
  export { default as GanttChart } from './components/organisms/GanttChart/GanttChart.svelte';
401
407
  export { default as ToastContainer } from './components/organisms/Toast/ToastContainer.svelte';
402
408
  export { toastStore, toast } from './components/organisms/Toast/toast.svelte.js';
@@ -491,6 +497,7 @@ export { default as AuthTemplate } from './components/templates/AuthTemplate/Aut
491
497
  export { default as DashboardTemplate } from './components/templates/DashboardTemplate/DashboardTemplate.svelte';
492
498
  export { default as MarketingTemplate } from './components/templates/MarketingTemplate/MarketingTemplate.svelte';
493
499
  export { default as ListingTemplate } from './components/templates/ListingTemplate/ListingTemplate.svelte';
500
+ export { default as ResourceStudioTemplate } from './components/templates/ResourceStudioTemplate/ResourceStudioTemplate.svelte';
494
501
  export { default as CheckoutTemplate } from './components/templates/CheckoutTemplate/CheckoutTemplate.svelte';
495
502
  export { default as DesktopTemplate } from './components/templates/DesktopTemplate/DesktopTemplate.svelte';
496
503
  // ── Utils ────────────────────────────────────────────────────────────────────
@@ -498,6 +505,7 @@ export { themeStore } from './utils/theme.svelte.js';
498
505
  export { pageVisibility } from './utils/pageVisibility.svelte.js';
499
506
  export { network } from './utils/network.svelte.js';
500
507
  export { DEFAULT_COLS, DEFAULT_ROW_HEIGHT, DEFAULT_GAP, GRID_DENSITY, clampItem, rectsOverlap, findCollisions, resolveCollisions, compactLayout, updateItem, addItem, removeItem, serializeLayout, parseLayout, layoutBounds, rescaleLayout, resizeItemByEdge } from './utils/layoutGrid.js';
508
+ export { attachMarqueeSelect, collectMarqueeItems, defaultMarqueeIgnore, getMarqueeModifier, hitTestIds, normalizeRect, rectsIntersect, resolveMarqueeSelection } from './utils/marqueeSelect.js';
501
509
  export { encodeQR, qrToSvgPath, punchLogoHole } from './utils/qr.js';
502
510
  export { renderMarkdown, renderInline, escapeHtml, splitTableRow, slugifyHeading } from './utils/markdown.js';
503
511
  export { diffLines, diffWords, diffStats, toSplitRows } from './utils/diff.js';
@@ -0,0 +1,48 @@
1
+ /** Axis-aligned selection rectangle (container-local coordinates). */
2
+ export interface MarqueeRect {
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ }
8
+ /** Hit-test target with an id and box in the same coordinate space as {@link MarqueeRect}. */
9
+ export interface MarqueeItemRect {
10
+ id: string;
11
+ x: number;
12
+ y: number;
13
+ width: number;
14
+ height: number;
15
+ }
16
+ export type MarqueeModifier = 'replace' | 'add' | 'toggle';
17
+ export interface MarqueeDragState {
18
+ rect: MarqueeRect;
19
+ ids: string[];
20
+ modifier: MarqueeModifier;
21
+ }
22
+ export interface AttachMarqueeSelectOptions {
23
+ /** Pixels of movement before a drag becomes a marquee (keeps clicks clean). Default 4. */
24
+ threshold?: number;
25
+ /** Supply item boxes; called on each move after threshold. */
26
+ getItems: () => MarqueeItemRect[];
27
+ /** Live updates while dragging (after threshold). */
28
+ onDrag?: (state: MarqueeDragState) => void;
29
+ /** Fired on pointerup if a marquee drag occurred. */
30
+ onSelect: (state: MarqueeDragState) => void;
31
+ /** Skip starting a marquee from this event target. */
32
+ shouldIgnore?: (target: EventTarget | null) => boolean;
33
+ /** Called when the overlay rect changes (including null when idle / below threshold). */
34
+ onRect?: (rect: MarqueeRect | null) => void;
35
+ }
36
+ export declare function normalizeRect(x0: number, y0: number, x1: number, y1: number): MarqueeRect;
37
+ export declare function rectsIntersect(a: MarqueeRect, b: MarqueeRect): boolean;
38
+ export declare function hitTestIds(marquee: MarqueeRect, items: MarqueeItemRect[]): string[];
39
+ export declare function getMarqueeModifier(e: Pick<PointerEvent, 'shiftKey' | 'metaKey' | 'ctrlKey'>): MarqueeModifier;
40
+ export declare function resolveMarqueeSelection(hitIds: string[], previousIds: string[], modifier: MarqueeModifier): string[];
41
+ /** Collect `[data-marquee-id]` children in container-local (incl. scroll) coordinates. */
42
+ export declare function collectMarqueeItems(container: HTMLElement, selector?: string): MarqueeItemRect[];
43
+ export declare function defaultMarqueeIgnore(target: EventTarget | null): boolean;
44
+ /**
45
+ * Attach Windows-style rubber-band selection to a container.
46
+ * Returns a disposer.
47
+ */
48
+ export declare function attachMarqueeSelect(container: HTMLElement, options: AttachMarqueeSelectOptions): () => void;
@@ -0,0 +1,189 @@
1
+ export function normalizeRect(x0, y0, x1, y1) {
2
+ const left = Math.min(x0, x1);
3
+ const top = Math.min(y0, y1);
4
+ return {
5
+ x: left,
6
+ y: top,
7
+ width: Math.abs(x1 - x0),
8
+ height: Math.abs(y1 - y0)
9
+ };
10
+ }
11
+ export function rectsIntersect(a, b) {
12
+ return (a.x < b.x + b.width &&
13
+ a.x + a.width > b.x &&
14
+ a.y < b.y + b.height &&
15
+ a.y + a.height > b.y);
16
+ }
17
+ export function hitTestIds(marquee, items) {
18
+ const ids = [];
19
+ for (const item of items) {
20
+ if (rectsIntersect(marquee, item))
21
+ ids.push(item.id);
22
+ }
23
+ return ids;
24
+ }
25
+ export function getMarqueeModifier(e) {
26
+ if (e.metaKey || e.ctrlKey)
27
+ return 'toggle';
28
+ if (e.shiftKey)
29
+ return 'add';
30
+ return 'replace';
31
+ }
32
+ export function resolveMarqueeSelection(hitIds, previousIds, modifier) {
33
+ if (modifier === 'replace')
34
+ return [...hitIds];
35
+ if (modifier === 'add')
36
+ return [...new Set([...previousIds, ...hitIds])];
37
+ const set = new Set(previousIds);
38
+ for (const id of hitIds) {
39
+ if (set.has(id))
40
+ set.delete(id);
41
+ else
42
+ set.add(id);
43
+ }
44
+ return [...set];
45
+ }
46
+ /** Collect `[data-marquee-id]` children in container-local (incl. scroll) coordinates. */
47
+ export function collectMarqueeItems(container, selector = '[data-marquee-id]') {
48
+ const cRect = container.getBoundingClientRect();
49
+ const nodes = container.querySelectorAll(selector);
50
+ const items = [];
51
+ for (const node of nodes) {
52
+ if (!(node instanceof HTMLElement))
53
+ continue;
54
+ const id = node.getAttribute('data-marquee-id');
55
+ if (!id)
56
+ continue;
57
+ const r = node.getBoundingClientRect();
58
+ items.push({
59
+ id,
60
+ x: r.left - cRect.left + container.scrollLeft,
61
+ y: r.top - cRect.top + container.scrollTop,
62
+ width: r.width,
63
+ height: r.height
64
+ });
65
+ }
66
+ return items;
67
+ }
68
+ export function defaultMarqueeIgnore(target) {
69
+ if (!(target instanceof Element))
70
+ return false;
71
+ return Boolean(target.closest('input, textarea, select, button, a, label, [data-marquee-ignore], [role="separator"], [contenteditable="true"]'));
72
+ }
73
+ function pointerInContainer(e, container) {
74
+ const r = container.getBoundingClientRect();
75
+ return {
76
+ x: e.clientX - r.left + container.scrollLeft,
77
+ y: e.clientY - r.top + container.scrollTop
78
+ };
79
+ }
80
+ /**
81
+ * Attach Windows-style rubber-band selection to a container.
82
+ * Returns a disposer.
83
+ */
84
+ export function attachMarqueeSelect(container, options) {
85
+ const threshold = options.threshold ?? 4;
86
+ const shouldIgnore = options.shouldIgnore ?? defaultMarqueeIgnore;
87
+ let originX = 0;
88
+ let originY = 0;
89
+ let active = false;
90
+ let dragging = false;
91
+ let pointerId = null;
92
+ function unbindWindow() {
93
+ window.removeEventListener('pointermove', onPointerMove);
94
+ window.removeEventListener('pointerup', onPointerUp);
95
+ window.removeEventListener('pointercancel', onPointerUp);
96
+ }
97
+ function finish(e) {
98
+ if (!active)
99
+ return;
100
+ active = false;
101
+ const wasDragging = dragging;
102
+ dragging = false;
103
+ unbindWindow();
104
+ if (pointerId != null) {
105
+ try {
106
+ container.releasePointerCapture(pointerId);
107
+ }
108
+ catch {
109
+ /* already released */
110
+ }
111
+ pointerId = null;
112
+ }
113
+ if (!wasDragging) {
114
+ options.onRect?.(null);
115
+ return;
116
+ }
117
+ const pt = pointerInContainer(e, container);
118
+ const rect = normalizeRect(originX, originY, pt.x, pt.y);
119
+ const ids = hitTestIds(rect, options.getItems());
120
+ const modifier = getMarqueeModifier(e);
121
+ options.onSelect({ rect, ids, modifier });
122
+ options.onRect?.(null);
123
+ window.getSelection()?.removeAllRanges();
124
+ }
125
+ function onPointerDown(e) {
126
+ if (e.button !== 0)
127
+ return;
128
+ if (shouldIgnore(e.target))
129
+ return;
130
+ const pt = pointerInContainer(e, container);
131
+ originX = pt.x;
132
+ originY = pt.y;
133
+ active = true;
134
+ dragging = false;
135
+ pointerId = e.pointerId;
136
+ // Defer capture until drag threshold so cell/row clicks still fire.
137
+ window.addEventListener('pointermove', onPointerMove);
138
+ window.addEventListener('pointerup', onPointerUp);
139
+ window.addEventListener('pointercancel', onPointerUp);
140
+ }
141
+ function onPointerMove(e) {
142
+ if (!active)
143
+ return;
144
+ if (pointerId != null && e.pointerId !== pointerId)
145
+ return;
146
+ const pt = pointerInContainer(e, container);
147
+ const dx = pt.x - originX;
148
+ const dy = pt.y - originY;
149
+ if (!dragging) {
150
+ if (Math.hypot(dx, dy) < threshold)
151
+ return;
152
+ dragging = true;
153
+ if (pointerId != null) {
154
+ try {
155
+ container.setPointerCapture(pointerId);
156
+ }
157
+ catch {
158
+ /* ignore */
159
+ }
160
+ }
161
+ e.preventDefault();
162
+ window.getSelection()?.removeAllRanges();
163
+ }
164
+ else {
165
+ e.preventDefault();
166
+ }
167
+ const rect = normalizeRect(originX, originY, pt.x, pt.y);
168
+ options.onRect?.(rect);
169
+ const ids = hitTestIds(rect, options.getItems());
170
+ const modifier = getMarqueeModifier(e);
171
+ options.onDrag?.({ rect, ids, modifier });
172
+ }
173
+ function onPointerUp(e) {
174
+ if (pointerId != null && e.pointerId !== pointerId)
175
+ return;
176
+ finish(e);
177
+ }
178
+ function onLostCapture(e) {
179
+ if (e.pointerId === pointerId)
180
+ finish(e);
181
+ }
182
+ container.addEventListener('pointerdown', onPointerDown);
183
+ container.addEventListener('lostpointercapture', onLostCapture);
184
+ return () => {
185
+ unbindWindow();
186
+ container.removeEventListener('pointerdown', onPointerDown);
187
+ container.removeEventListener('lostpointercapture', onLostCapture);
188
+ };
189
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@r2digisolutions/components",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "private": false,
5
5
  "description": "R2DigiSolutions Svelte 5 component library — Atomic Design, Tailwind 4, Light/Dark mode",
6
6
  "license": "MIT",