@svgrid/grid 2.2.0 → 2.2.1
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.
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +178 -178
- package/dist/SvCarousel.svelte +141 -141
- package/dist/SvColorInput.svelte +187 -187
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvDrawer.svelte +250 -250
- package/dist/SvGrid.svelte +3131 -3131
- package/dist/SvGridBoard.svelte +2339 -2339
- package/dist/SvGridChart.svelte +1724 -1724
- package/dist/SvGridChartPanel.svelte +485 -485
- package/dist/SvGridDropdown.svelte +689 -689
- package/dist/SvGroupCell.svelte +109 -109
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +117 -117
- package/dist/SvNumberInput.svelte +131 -131
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPopover.svelte +139 -139
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvTooltip.svelte +127 -127
- package/dist/SvTour.svelte +204 -204
- package/dist/SvTree.svelte +377 -377
- package/dist/SvTreeSelect.svelte +230 -230
- package/dist/cdn/svgrid.js +8 -8
- package/dist/cdn/svgrid.svelte-external.js +8 -8
- package/dist/chart-export.js +8 -8
- package/package.json +10 -10
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +178 -178
- package/src/SvCarousel.svelte +141 -141
- package/src/SvColorInput.svelte +187 -187
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvDrawer.svelte +250 -250
- package/src/SvGrid.controller.svelte.ts +3158 -3158
- package/src/SvGrid.svelte +3131 -3131
- package/src/SvGrid.types.ts +1489 -1489
- package/src/SvGridBoard.svelte +2339 -2339
- package/src/SvGridChart.svelte +1724 -1724
- package/src/SvGridChartPanel.svelte +485 -485
- package/src/SvGridDropdown.svelte +689 -689
- package/src/SvGroupCell.svelte +109 -109
- package/src/SvMenu.svelte +124 -124
- package/src/SvMenu.test.ts +97 -97
- package/src/SvMenuList.svelte +117 -117
- package/src/SvNumberInput.svelte +131 -131
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPopover.svelte +139 -139
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvTooltip.svelte +127 -127
- package/src/SvTour.svelte +204 -204
- package/src/SvTree.svelte +377 -377
- package/src/SvTreeSelect.svelte +230 -230
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- package/src/build-api.ts +870 -870
- package/src/cell-formatting.ts +169 -169
- package/src/cell-render.ts +469 -469
- package/src/chart-export.ts +201 -201
- package/src/chart.ts +2296 -2296
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1111 -1111
- package/src/createAutocomplete.svelte.ts +132 -132
- package/src/createCombobox.svelte.ts +179 -179
- package/src/createCountryInput.svelte.ts +157 -157
- package/src/createDropdownList.svelte.ts +162 -162
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/createMenu.svelte.ts +202 -202
- package/src/createPopoverSelect.svelte.ts +206 -206
- package/src/createTree.svelte.ts +319 -319
- package/src/editing.test.ts +859 -859
- package/src/editing.ts +675 -675
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +598 -598
- package/src/flex-render.ts +3 -3
- package/src/index.ts +623 -623
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/list-nav.test.ts +49 -49
- package/src/list-nav.ts +29 -29
- package/src/menus.ts +639 -639
- package/src/merge-objects.ts +48 -48
- package/src/overlays.test.ts +90 -90
- package/src/render-component.ts +28 -28
- package/src/selection.test.ts +754 -754
- package/src/selection.ts +600 -600
- package/src/server-data-source.test.ts +289 -289
- package/src/server-data-source.ts +413 -413
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +489 -489
- package/src/spreadsheet.ts +312 -312
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/svgrid-wrapper.types.ts +501 -501
- package/src/svgrid.behavior.test.ts +706 -706
- package/src/svgrid.charting.test.ts +527 -527
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.wrapper.test.ts +40 -40
- package/src/test-setup.ts +62 -62
- package/src/themes/index.ts +159 -159
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +296 -296
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
export type { MenuItem } from './SvMenuList.svelte'
|
|
3
|
-
</script>
|
|
4
|
-
|
|
5
|
-
<script lang="ts">
|
|
6
|
-
/**
|
|
7
|
-
* SvContextMenu - wraps a region and opens a menu at the pointer on right-click
|
|
8
|
-
* (or long-press). Reuses SvMenuList (submenus, keyboard, icons, shortcuts),
|
|
9
|
-
* portals to <body>, and closes via the shared dismissable layer stack.
|
|
10
|
-
* Parity: Smart context-menu.
|
|
11
|
-
*
|
|
12
|
-
* ```svelte
|
|
13
|
-
* <SvContextMenu items={items} onSelect={(i) => run(i)}>
|
|
14
|
-
* <div class="drop-zone">Right-click me</div>
|
|
15
|
-
* </SvContextMenu>
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
import type { Snippet } from 'svelte'
|
|
19
|
-
import { portalToBody, popIn } from './popover'
|
|
20
|
-
import { createDismissableLayer } from './a11y/dismissable'
|
|
21
|
-
import { createFocusTrap } from './a11y/focus-trap'
|
|
22
|
-
import SvMenuList, { type MenuItem } from './SvMenuList.svelte'
|
|
23
|
-
|
|
24
|
-
type Props = {
|
|
25
|
-
items: ReadonlyArray<MenuItem>
|
|
26
|
-
onSelect?: (item: MenuItem) => void
|
|
27
|
-
disabled?: boolean
|
|
28
|
-
ariaLabel?: string
|
|
29
|
-
children?: Snippet
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
let { items, onSelect, disabled = false, ariaLabel, children }: Props = $props()
|
|
33
|
-
|
|
34
|
-
let open = $state(false)
|
|
35
|
-
let pos = $state({ x: 0, y: 0 })
|
|
36
|
-
let panelEl = $state<HTMLDivElement | null>(null)
|
|
37
|
-
let zoneEl = $state<HTMLDivElement | null>(null)
|
|
38
|
-
|
|
39
|
-
const MENU_W = 200
|
|
40
|
-
function openAt(clientX: number, clientY: number) {
|
|
41
|
-
// Clamp so the menu stays on-screen (estimate height from item count).
|
|
42
|
-
const estH = Math.min(items.length, 12) * 34 + 8
|
|
43
|
-
const x = Math.min(clientX, window.innerWidth - MENU_W - 6)
|
|
44
|
-
const y = Math.min(clientY, window.innerHeight - estH - 6)
|
|
45
|
-
pos = { x: Math.max(6, x), y: Math.max(6, y) }
|
|
46
|
-
open = true
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function onContextMenu(e: MouseEvent) {
|
|
50
|
-
if (disabled) return
|
|
51
|
-
e.preventDefault()
|
|
52
|
-
openAt(e.clientX, e.clientY)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Keyboard equivalent of a right-click: the ContextMenu key, or Shift+F10.
|
|
56
|
-
// Anchors at the focused zone's own position instead of the (nonexistent)
|
|
57
|
-
// pointer coordinates.
|
|
58
|
-
function onZoneKeydown(e: KeyboardEvent) {
|
|
59
|
-
if (disabled) return
|
|
60
|
-
const isMenuKey = e.key === 'ContextMenu' || (e.key === 'F10' && e.shiftKey)
|
|
61
|
-
if (!isMenuKey) return
|
|
62
|
-
e.preventDefault()
|
|
63
|
-
const rect = zoneEl?.getBoundingClientRect()
|
|
64
|
-
openAt(rect ? rect.left : 0, rect ? rect.bottom : 0)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
$effect(() => {
|
|
68
|
-
if (!open || !panelEl) return
|
|
69
|
-
const trap = createFocusTrap(panelEl, {
|
|
70
|
-
initialFocus: () => panelEl?.querySelector<HTMLElement>('[role="menuitem"]:not([disabled])') ?? null,
|
|
71
|
-
})
|
|
72
|
-
trap.activate()
|
|
73
|
-
const layer = createDismissableLayer({
|
|
74
|
-
element: () => panelEl,
|
|
75
|
-
onDismiss: () => (open = false),
|
|
76
|
-
})
|
|
77
|
-
layer.activate()
|
|
78
|
-
// Reposition-close on scroll (a moved anchor would leave the menu stranded).
|
|
79
|
-
const onScroll = () => (open = false)
|
|
80
|
-
window.addEventListener('scroll', onScroll, true)
|
|
81
|
-
return () => { layer.release(); trap.release(); window.removeEventListener('scroll', onScroll, true) }
|
|
82
|
-
})
|
|
83
|
-
</script>
|
|
84
|
-
|
|
85
|
-
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_no_noninteractive_tabindex -->
|
|
86
|
-
<div class="sv-ctx__zone" bind:this={zoneEl} tabindex={disabled ? -1 : 0} oncontextmenu={onContextMenu} onkeydown={onZoneKeydown}>
|
|
87
|
-
{@render children?.()}
|
|
88
|
-
</div>
|
|
89
|
-
|
|
90
|
-
{#if open}
|
|
91
|
-
<div
|
|
92
|
-
bind:this={panelEl}
|
|
93
|
-
class="sv-ctx"
|
|
94
|
-
use:portalToBody
|
|
95
|
-
use:popIn={{}}
|
|
96
|
-
style:position="fixed"
|
|
97
|
-
style:top={`${pos.y}px`}
|
|
98
|
-
style:left={`${pos.x}px`}
|
|
99
|
-
aria-label={ariaLabel}
|
|
100
|
-
>
|
|
101
|
-
<SvMenuList {items} onclose={() => (open = false)} onselect={(i) => onSelect?.(i)} />
|
|
102
|
-
</div>
|
|
103
|
-
{/if}
|
|
104
|
-
|
|
105
|
-
<style>
|
|
106
|
-
/* display:contents generates no box and per spec cannot receive focus - it
|
|
107
|
-
must be a real box for the keyboard-open path (tabindex) to work at all. */
|
|
108
|
-
.sv-ctx__zone { display: block; }
|
|
109
|
-
.sv-ctx__zone:focus-visible { outline: 2px solid var(--sg-accent, #6366f1); outline-offset: 1px; }
|
|
110
|
-
:global(.sv-ctx) {
|
|
111
|
-
z-index: 2147483646; min-width: 200px;
|
|
112
|
-
background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
113
|
-
border: 1px solid var(--sg-border, #e2e8f0); border-radius: 10px;
|
|
114
|
-
box-shadow: 0 16px 48px -12px rgba(15, 23, 42, 0.35); font-size: 13px;
|
|
115
|
-
}
|
|
116
|
-
</style>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type { MenuItem } from './SvMenuList.svelte'
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
/**
|
|
7
|
+
* SvContextMenu - wraps a region and opens a menu at the pointer on right-click
|
|
8
|
+
* (or long-press). Reuses SvMenuList (submenus, keyboard, icons, shortcuts),
|
|
9
|
+
* portals to <body>, and closes via the shared dismissable layer stack.
|
|
10
|
+
* Parity: Smart context-menu.
|
|
11
|
+
*
|
|
12
|
+
* ```svelte
|
|
13
|
+
* <SvContextMenu items={items} onSelect={(i) => run(i)}>
|
|
14
|
+
* <div class="drop-zone">Right-click me</div>
|
|
15
|
+
* </SvContextMenu>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { Snippet } from 'svelte'
|
|
19
|
+
import { portalToBody, popIn } from './popover'
|
|
20
|
+
import { createDismissableLayer } from './a11y/dismissable'
|
|
21
|
+
import { createFocusTrap } from './a11y/focus-trap'
|
|
22
|
+
import SvMenuList, { type MenuItem } from './SvMenuList.svelte'
|
|
23
|
+
|
|
24
|
+
type Props = {
|
|
25
|
+
items: ReadonlyArray<MenuItem>
|
|
26
|
+
onSelect?: (item: MenuItem) => void
|
|
27
|
+
disabled?: boolean
|
|
28
|
+
ariaLabel?: string
|
|
29
|
+
children?: Snippet
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let { items, onSelect, disabled = false, ariaLabel, children }: Props = $props()
|
|
33
|
+
|
|
34
|
+
let open = $state(false)
|
|
35
|
+
let pos = $state({ x: 0, y: 0 })
|
|
36
|
+
let panelEl = $state<HTMLDivElement | null>(null)
|
|
37
|
+
let zoneEl = $state<HTMLDivElement | null>(null)
|
|
38
|
+
|
|
39
|
+
const MENU_W = 200
|
|
40
|
+
function openAt(clientX: number, clientY: number) {
|
|
41
|
+
// Clamp so the menu stays on-screen (estimate height from item count).
|
|
42
|
+
const estH = Math.min(items.length, 12) * 34 + 8
|
|
43
|
+
const x = Math.min(clientX, window.innerWidth - MENU_W - 6)
|
|
44
|
+
const y = Math.min(clientY, window.innerHeight - estH - 6)
|
|
45
|
+
pos = { x: Math.max(6, x), y: Math.max(6, y) }
|
|
46
|
+
open = true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function onContextMenu(e: MouseEvent) {
|
|
50
|
+
if (disabled) return
|
|
51
|
+
e.preventDefault()
|
|
52
|
+
openAt(e.clientX, e.clientY)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Keyboard equivalent of a right-click: the ContextMenu key, or Shift+F10.
|
|
56
|
+
// Anchors at the focused zone's own position instead of the (nonexistent)
|
|
57
|
+
// pointer coordinates.
|
|
58
|
+
function onZoneKeydown(e: KeyboardEvent) {
|
|
59
|
+
if (disabled) return
|
|
60
|
+
const isMenuKey = e.key === 'ContextMenu' || (e.key === 'F10' && e.shiftKey)
|
|
61
|
+
if (!isMenuKey) return
|
|
62
|
+
e.preventDefault()
|
|
63
|
+
const rect = zoneEl?.getBoundingClientRect()
|
|
64
|
+
openAt(rect ? rect.left : 0, rect ? rect.bottom : 0)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
$effect(() => {
|
|
68
|
+
if (!open || !panelEl) return
|
|
69
|
+
const trap = createFocusTrap(panelEl, {
|
|
70
|
+
initialFocus: () => panelEl?.querySelector<HTMLElement>('[role="menuitem"]:not([disabled])') ?? null,
|
|
71
|
+
})
|
|
72
|
+
trap.activate()
|
|
73
|
+
const layer = createDismissableLayer({
|
|
74
|
+
element: () => panelEl,
|
|
75
|
+
onDismiss: () => (open = false),
|
|
76
|
+
})
|
|
77
|
+
layer.activate()
|
|
78
|
+
// Reposition-close on scroll (a moved anchor would leave the menu stranded).
|
|
79
|
+
const onScroll = () => (open = false)
|
|
80
|
+
window.addEventListener('scroll', onScroll, true)
|
|
81
|
+
return () => { layer.release(); trap.release(); window.removeEventListener('scroll', onScroll, true) }
|
|
82
|
+
})
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_no_noninteractive_tabindex -->
|
|
86
|
+
<div class="sv-ctx__zone" bind:this={zoneEl} tabindex={disabled ? -1 : 0} oncontextmenu={onContextMenu} onkeydown={onZoneKeydown}>
|
|
87
|
+
{@render children?.()}
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
{#if open}
|
|
91
|
+
<div
|
|
92
|
+
bind:this={panelEl}
|
|
93
|
+
class="sv-ctx"
|
|
94
|
+
use:portalToBody
|
|
95
|
+
use:popIn={{}}
|
|
96
|
+
style:position="fixed"
|
|
97
|
+
style:top={`${pos.y}px`}
|
|
98
|
+
style:left={`${pos.x}px`}
|
|
99
|
+
aria-label={ariaLabel}
|
|
100
|
+
>
|
|
101
|
+
<SvMenuList {items} onclose={() => (open = false)} onselect={(i) => onSelect?.(i)} />
|
|
102
|
+
</div>
|
|
103
|
+
{/if}
|
|
104
|
+
|
|
105
|
+
<style>
|
|
106
|
+
/* display:contents generates no box and per spec cannot receive focus - it
|
|
107
|
+
must be a real box for the keyboard-open path (tabindex) to work at all. */
|
|
108
|
+
.sv-ctx__zone { display: block; }
|
|
109
|
+
.sv-ctx__zone:focus-visible { outline: 2px solid var(--sg-accent, #6366f1); outline-offset: 1px; }
|
|
110
|
+
:global(.sv-ctx) {
|
|
111
|
+
z-index: 2147483646; min-width: 200px;
|
|
112
|
+
background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
113
|
+
border: 1px solid var(--sg-border, #e2e8f0); border-radius: 10px;
|
|
114
|
+
box-shadow: 0 16px 48px -12px rgba(15, 23, 42, 0.35); font-size: 13px;
|
|
115
|
+
}
|
|
116
|
+
</style>
|