@svgrid/grid 2.1.22 → 2.1.23
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/README.md +9 -2
- package/dist/DockNodeView.svelte +306 -0
- package/dist/DockNodeView.svelte.d.ts +7 -0
- package/dist/DockPopoutHost.svelte +17 -0
- package/dist/DockPopoutHost.svelte.d.ts +14 -0
- package/dist/GridMenus.svelte +83 -3
- package/dist/SvAutoComplete.svelte +5 -3
- package/dist/SvCarousel.svelte +20 -42
- package/dist/SvColorInput.svelte +4 -3
- package/dist/SvColorInput.svelte.d.ts +1 -1
- package/dist/SvComboBox.svelte +7 -3
- package/dist/SvCommand.svelte +28 -85
- package/dist/SvCountryInput.svelte +1 -1
- package/dist/SvDateRangeInput.svelte +1 -1
- package/dist/SvDateTimePicker.svelte +9 -3
- package/dist/SvDateTimePicker.svelte.d.ts +3 -0
- package/dist/SvDockLayout.svelte +244 -0
- package/dist/SvDockLayout.svelte.d.ts +77 -0
- package/dist/SvDockManager.svelte +922 -0
- package/dist/SvDockManager.svelte.d.ts +106 -0
- package/dist/SvDrawer.svelte +69 -47
- package/dist/SvDrawer.svelte.d.ts +4 -0
- package/dist/SvDropDownList.svelte +85 -13
- package/dist/SvDropDownList.svelte.d.ts +3 -0
- package/dist/SvField.svelte +21 -4
- package/dist/SvField.svelte.d.ts +1 -0
- package/dist/SvForm.svelte +201 -91
- package/dist/SvForm.svelte.d.ts +15 -22
- package/dist/SvGrid.controller.svelte.d.ts +32 -1
- package/dist/SvGrid.controller.svelte.js +156 -27
- package/dist/SvGrid.css +193 -5
- package/dist/SvGrid.svelte +357 -12
- package/dist/SvGrid.types.d.ts +452 -1
- package/dist/SvGridDropdown.svelte +8 -1
- package/dist/SvGridSelect.svelte +1 -0
- package/dist/SvListBox.svelte +36 -14
- package/dist/SvMaskedInput.svelte +4 -3
- package/dist/SvMaskedInput.svelte.d.ts +1 -1
- package/dist/SvMenu.svelte +1 -1
- package/dist/SvMenuList.svelte +23 -71
- package/dist/SvMenuList.svelte.d.ts +2 -13
- package/dist/SvModal.svelte +12 -29
- package/dist/SvMultiSelect.svelte +1 -0
- package/dist/SvNumberInput.svelte +4 -3
- package/dist/SvNumberInput.svelte.d.ts +1 -1
- package/dist/SvPagination.svelte +18 -18
- package/dist/SvPasswordInput.svelte +3 -2
- package/dist/SvPasswordInput.svelte.d.ts +1 -1
- package/dist/SvPhoneInput.svelte +3 -2
- package/dist/SvPhoneInput.svelte.d.ts +1 -1
- package/dist/SvPopover.svelte +1 -1
- package/dist/SvStepper.svelte +6 -9
- package/dist/SvTagsInput.svelte +3 -2
- package/dist/SvTagsInput.svelte.d.ts +1 -1
- package/dist/SvTooltip.svelte +12 -23
- package/dist/SvTour.svelte +32 -4
- package/dist/SvTreeSelect.svelte +1 -0
- package/dist/build-api.js +76 -54
- package/dist/cdn/svgrid.js +10907 -6444
- package/dist/cdn/svgrid.svelte-external.js +9622 -5475
- package/dist/columns.js +6 -0
- package/dist/conditional-formatting.d.ts +67 -3
- package/dist/conditional-formatting.js +124 -17
- package/dist/createCarousel.svelte.d.ts +72 -0
- package/dist/createCarousel.svelte.js +80 -0
- package/dist/createCommand.svelte.d.ts +62 -0
- package/dist/createCommand.svelte.js +109 -0
- package/dist/createForm.svelte.d.ts +62 -0
- package/dist/createForm.svelte.js +374 -0
- package/dist/createMenu.svelte.d.ts +88 -0
- package/dist/createMenu.svelte.js +153 -0
- package/dist/createOverlay.svelte.d.ts +38 -0
- package/dist/createOverlay.svelte.js +68 -0
- package/dist/createPagination.d.ts +74 -0
- package/dist/createPagination.js +71 -0
- package/dist/createPopoverSelect.svelte.js +6 -12
- package/dist/createStepper.d.ts +44 -0
- package/dist/createStepper.js +26 -0
- package/dist/createTooltip.svelte.d.ts +32 -0
- package/dist/createTooltip.svelte.js +61 -0
- package/dist/datetime/timezone.d.ts +36 -0
- package/dist/datetime/timezone.js +110 -0
- package/dist/dock-context.d.ts +52 -0
- package/dist/dock-context.js +1 -0
- package/dist/dock-manager-model.d.ts +136 -0
- package/dist/dock-manager-model.js +514 -0
- package/dist/dock-model.d.ts +105 -0
- package/dist/dock-model.js +288 -0
- package/dist/dock-popout.d.ts +30 -0
- package/dist/dock-popout.js +81 -0
- package/dist/editor-contract.d.ts +2 -0
- package/dist/filter-operators.d.ts +6 -0
- package/dist/filter-operators.js +35 -2
- package/dist/filtering/excel-filters.d.ts +18 -1
- package/dist/filtering/excel-filters.js +56 -0
- package/dist/form-field.d.ts +73 -0
- package/dist/form-field.js +15 -0
- package/dist/index.d.ts +25 -5
- package/dist/index.js +40 -4
- package/dist/list-nav.d.ts +18 -0
- package/dist/list-nav.js +30 -0
- package/dist/list-option.d.ts +2 -0
- package/dist/menu-item.d.ts +20 -0
- package/dist/menu-item.js +1 -0
- package/dist/menus.d.ts +5 -0
- package/dist/menus.js +68 -16
- package/dist/panel-resize.d.ts +25 -0
- package/dist/panel-resize.js +40 -0
- package/dist/popover.d.ts +29 -3
- package/dist/popover.js +22 -6
- package/dist/recurrence.d.ts +33 -7
- package/dist/recurrence.js +151 -13
- package/dist/row-drag.d.ts +8 -0
- package/dist/row-drag.js +11 -0
- package/dist/scheduler-ical.d.ts +30 -0
- package/dist/scheduler-ical.js +180 -0
- package/dist/scheduler-model.d.ts +269 -0
- package/dist/scheduler-model.js +602 -0
- package/dist/scheduler-view.svelte.d.ts +22 -0
- package/dist/scheduler-view.svelte.js +13 -0
- package/dist/spreadsheet.js +20 -12
- package/dist/ui-app.types.d.ts +3 -0
- package/package.json +5 -1
- package/src/DockNodeView.svelte +306 -0
- package/src/DockPopoutHost.svelte +17 -0
- package/src/GridMenus.svelte +83 -3
- package/src/SvAutoComplete.svelte +5 -3
- package/src/SvCarousel.svelte +20 -42
- package/src/SvCarousel.test.ts +68 -0
- package/src/SvColorInput.svelte +4 -3
- package/src/SvComboBox.svelte +7 -3
- package/src/SvCommand.svelte +28 -85
- package/src/SvCommand.test.ts +78 -0
- package/src/SvCountryInput.svelte +1 -1
- package/src/SvDateRangeInput.svelte +1 -1
- package/src/SvDateTimePicker.svelte +9 -3
- package/src/SvDockLayout.svelte +244 -0
- package/src/SvDockManager.svelte +922 -0
- package/src/SvDrawer.svelte +69 -47
- package/src/SvDropDownList.svelte +85 -13
- package/src/SvField.svelte +21 -4
- package/src/SvForm.svelte +201 -91
- package/src/SvForm.test.ts +299 -0
- package/src/SvGrid.controller.svelte.ts +152 -28
- package/src/SvGrid.css +193 -5
- package/src/SvGrid.svelte +357 -12
- package/src/SvGrid.types.ts +463 -1
- package/src/SvGridDropdown.svelte +8 -1
- package/src/SvGridSelect.svelte +1 -0
- package/src/SvListBox.svelte +36 -14
- package/src/SvMaskedInput.svelte +4 -3
- package/src/SvMenu.svelte +1 -1
- package/src/SvMenu.test.ts +24 -0
- package/src/SvMenuList.svelte +23 -71
- package/src/SvModal.svelte +12 -29
- package/src/SvModal.test.ts +82 -0
- package/src/SvMultiSelect.svelte +1 -0
- package/src/SvNumberInput.svelte +4 -3
- package/src/SvPagination.svelte +18 -18
- package/src/SvPasswordInput.svelte +3 -2
- package/src/SvPhoneInput.svelte +3 -2
- package/src/SvPopover.svelte +1 -1
- package/src/SvStepper.svelte +6 -9
- package/src/SvTagsInput.svelte +3 -2
- package/src/SvTooltip.svelte +12 -23
- package/src/SvTooltip.test.ts +73 -0
- package/src/SvTour.svelte +32 -4
- package/src/SvTreeSelect.svelte +1 -0
- package/src/build-api.ts +74 -51
- package/src/columns.ts +5 -0
- package/src/conditional-formatting.test.ts +104 -0
- package/src/conditional-formatting.ts +176 -19
- package/src/createCarousel.svelte.ts +104 -0
- package/src/createCommand.svelte.ts +125 -0
- package/src/createForm.svelte.ts +333 -0
- package/src/createMenu.svelte.ts +202 -0
- package/src/createOverlay.svelte.ts +101 -0
- package/src/createPagination.test.ts +65 -0
- package/src/createPagination.ts +84 -0
- package/src/createPopoverSelect.svelte.ts +5 -9
- package/src/createStepper.test.ts +62 -0
- package/src/createStepper.ts +61 -0
- package/src/createTooltip.svelte.ts +86 -0
- package/src/datetime/timezone.test.ts +69 -0
- package/src/datetime/timezone.ts +134 -0
- package/src/dock-context.ts +47 -0
- package/src/dock-manager-model.test.ts +275 -0
- package/src/dock-manager-model.ts +593 -0
- package/src/dock-model.test.ts +163 -0
- package/src/dock-model.ts +368 -0
- package/src/dock-popout.ts +94 -0
- package/src/editor-contract.ts +2 -0
- package/src/filter-operators.test.ts +59 -0
- package/src/filter-operators.ts +35 -2
- package/src/filtering/excel-filters.test.ts +86 -1
- package/src/filtering/excel-filters.ts +65 -0
- package/src/form-field.ts +87 -0
- package/src/index.ts +158 -4
- package/src/list-nav.test.ts +49 -0
- package/src/list-nav.ts +29 -0
- package/src/list-option.ts +2 -0
- package/src/menu-item.ts +21 -0
- package/src/menus.test.ts +48 -0
- package/src/menus.ts +73 -16
- package/src/panel-resize.ts +52 -0
- package/src/popover.bounds.test.ts +93 -0
- package/src/popover.ts +51 -7
- package/src/recurrence.test.ts +88 -1
- package/src/recurrence.ts +166 -15
- package/src/row-drag.ts +10 -0
- package/src/scheduler-ical.test.ts +68 -0
- package/src/scheduler-ical.ts +180 -0
- package/src/scheduler-model.test.ts +546 -0
- package/src/scheduler-model.ts +853 -0
- package/src/scheduler-view.svelte.ts +33 -0
- package/src/spreadsheet.ts +20 -12
- package/src/svgrid.effect-driven-filter.svelte.test.ts +116 -0
- package/src/ui-app.types.ts +3 -0
- package/src/virtual.test.ts +6 -4
package/dist/SvGrid.svelte
CHANGED
|
@@ -52,19 +52,26 @@
|
|
|
52
52
|
colorfulChipStyle,
|
|
53
53
|
getEditorClass,
|
|
54
54
|
} from "./SvGrid.helpers";
|
|
55
|
+
import {
|
|
56
|
+
splitInTokens,
|
|
57
|
+
joinInTokens,
|
|
58
|
+
} from "./filtering/excel-filters";
|
|
55
59
|
import { createSvGridController } from "./SvGrid.controller.svelte";
|
|
56
60
|
import GridMenus from "./GridMenus.svelte";
|
|
57
61
|
import SvGridChartPanel from "./SvGridChartPanel.svelte";
|
|
58
62
|
import GridFooter from "./GridFooter.svelte";
|
|
59
63
|
import SvGridBoard from "./SvGridBoard.svelte";
|
|
64
|
+
import { getSchedulerView } from "./scheduler-view.svelte";
|
|
60
65
|
let props: Props<TFeatures, TData> = $props();
|
|
61
66
|
const ctrl = createSvGridController(props);
|
|
62
67
|
// Kanban board mode: when `board` is set the grid renders lanes of cards
|
|
63
68
|
// instead of the table (see SvGridBoard). Narrowed derived so the template
|
|
64
69
|
// branch can pass it non-null.
|
|
65
70
|
const boardConfig = $derived(props.board);
|
|
66
|
-
//
|
|
67
|
-
|
|
71
|
+
// Scheduler / calendar mode: same "view of the grid" seam as the board.
|
|
72
|
+
const schedulerConfig = $derived(props.scheduler);
|
|
73
|
+
// The board / scheduler render the grid's FILTERED + SORTED rows (not raw
|
|
74
|
+
// data), so the search box / column filters / sort all flow through to them.
|
|
68
75
|
const boardData = $derived(
|
|
69
76
|
ctrl.allRowsBeforePagination
|
|
70
77
|
.filter((r) => !ctrl.isGroupRow(r))
|
|
@@ -101,7 +108,7 @@
|
|
|
101
108
|
const grid = $derived(ctrl.grid);
|
|
102
109
|
const allColumns = $derived(ctrl.allColumns);
|
|
103
110
|
const headerGroups = $derived(ctrl.headerGroups);
|
|
104
|
-
const groupHeaderRows = $derived(ctrl.
|
|
111
|
+
const groupHeaderRows = $derived(ctrl.groupHeaderRowsWindowed);
|
|
105
112
|
const cellPinStyle = $derived(ctrl.cellPinStyle);
|
|
106
113
|
const isColumnPinned = $derived(ctrl.isColumnPinned);
|
|
107
114
|
const colDragId = $derived(ctrl.colDragId);
|
|
@@ -172,6 +179,13 @@
|
|
|
172
179
|
const toggleGroupInPanel = $derived(ctrl.toggleGroupInPanel);
|
|
173
180
|
const virtualizer = $derived(ctrl.virtualizer);
|
|
174
181
|
const rowVirtualizationEnabled = $derived(ctrl.rowVirtualizationEnabled);
|
|
182
|
+
// The fixed row height, matching what the virtualized path takes from the virtualizer.
|
|
183
|
+
// The non-virtualized (`virtualization={false}`) body must apply this too, else its rows
|
|
184
|
+
// fall back to content height and look shorter than a virtualized grid's.
|
|
185
|
+
const rowSizePx = (i: number): number => {
|
|
186
|
+
const rh = props.rowHeight;
|
|
187
|
+
return typeof rh === "function" ? rh(i) : (rh ?? 30);
|
|
188
|
+
};
|
|
175
189
|
const columnVirtualizationEnabled = $derived(
|
|
176
190
|
ctrl.columnVirtualizationEnabled,
|
|
177
191
|
);
|
|
@@ -367,7 +381,149 @@
|
|
|
367
381
|
const openColumnMenu = $derived(ctrl.openColumnMenu);
|
|
368
382
|
const openFilterMenu = $derived(ctrl.openFilterMenu);
|
|
369
383
|
const openOperatorMenu = $derived(ctrl.openOperatorMenu);
|
|
384
|
+
const openInSuggest = $derived(ctrl.openInSuggest);
|
|
385
|
+
const closeInSuggest = $derived(ctrl.closeInSuggest);
|
|
386
|
+
const addFilterToken = $derived(ctrl.addFilterToken);
|
|
387
|
+
const removeFilterToken = $derived(ctrl.removeFilterToken);
|
|
388
|
+
const facetValuesForColumn = $derived(ctrl.facetValuesForColumn);
|
|
370
389
|
const onWindowKeydown = $derived(ctrl.onWindowKeydown);
|
|
390
|
+
|
|
391
|
+
// True when a `regex` filter's pattern won't compile - used to flag the
|
|
392
|
+
// input so the user sees a half-typed pattern isn't being applied.
|
|
393
|
+
function isInvalidRegex(pattern: string): boolean {
|
|
394
|
+
if (!pattern) return false;
|
|
395
|
+
try {
|
|
396
|
+
new RegExp(pattern);
|
|
397
|
+
return false;
|
|
398
|
+
} catch {
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Commit the token currently typed in an `in` / `notIn` chip input,
|
|
404
|
+
// appending it to the column's token list and clearing the box.
|
|
405
|
+
function commitFilterChip(columnId: string, input: HTMLInputElement): void {
|
|
406
|
+
const raw = input.value.trim();
|
|
407
|
+
if (!raw) return;
|
|
408
|
+
addFilterToken(columnId, raw);
|
|
409
|
+
input.value = "";
|
|
410
|
+
ctrl.inSuggestQuery = "";
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function removeFilterChip(columnId: string, token: string): void {
|
|
414
|
+
removeFilterToken(columnId, token);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Focus opens the value-suggestions dropdown under this input; typing
|
|
418
|
+
// narrows it via `inSuggestQuery`.
|
|
419
|
+
function onFilterChipFocus(
|
|
420
|
+
event: FocusEvent,
|
|
421
|
+
columnId: string,
|
|
422
|
+
): void {
|
|
423
|
+
openInSuggest(event.currentTarget as HTMLInputElement, columnId);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function onFilterChipInput(
|
|
427
|
+
event: Event,
|
|
428
|
+
columnId: string,
|
|
429
|
+
): void {
|
|
430
|
+
const input = event.currentTarget as HTMLInputElement;
|
|
431
|
+
ctrl.inSuggestQuery = input.value;
|
|
432
|
+
// Reopen the dropdown if a prior selection/blur had closed it.
|
|
433
|
+
if (ctrl.inSuggestFor !== columnId) openInSuggest(input, columnId);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Overflow layout for the `in` / `notIn` chip row: render every chip, then
|
|
437
|
+
// measure how many fit on one line and collapse the rest into the "+N" pill.
|
|
438
|
+
// A guess-by-width heuristic can't work (labels vary), so we measure real
|
|
439
|
+
// element widths and re-run on column resize (ResizeObserver) and whenever
|
|
440
|
+
// the token set or width changes (action param -> update()).
|
|
441
|
+
function fitChips(node: HTMLElement, _param?: string) {
|
|
442
|
+
const INPUT_MIN = 14; // small sliver kept clickable to focus + type
|
|
443
|
+
const recompute = () => {
|
|
444
|
+
const chips = Array.from(
|
|
445
|
+
node.querySelectorAll<HTMLElement>("[data-chip]"),
|
|
446
|
+
);
|
|
447
|
+
const more = node.querySelector<HTMLElement>("[data-more]");
|
|
448
|
+
if (!more) return;
|
|
449
|
+
// Reset to the all-visible baseline before measuring.
|
|
450
|
+
for (const chip of chips) chip.style.display = "";
|
|
451
|
+
more.style.display = "none";
|
|
452
|
+
if (!chips.length || node.clientWidth === 0) return;
|
|
453
|
+
|
|
454
|
+
const style = getComputedStyle(node);
|
|
455
|
+
const padX =
|
|
456
|
+
parseFloat(style.paddingLeft || "0") +
|
|
457
|
+
parseFloat(style.paddingRight || "0");
|
|
458
|
+
const gap = parseFloat(style.columnGap || style.gap || "0") || 3;
|
|
459
|
+
const avail = node.clientWidth - padX - INPUT_MIN;
|
|
460
|
+
|
|
461
|
+
// Do all chips fit without needing a "+N" pill?
|
|
462
|
+
let used = 0;
|
|
463
|
+
let fit = 0;
|
|
464
|
+
for (const chip of chips) {
|
|
465
|
+
const w = chip.offsetWidth + gap;
|
|
466
|
+
if (used + w <= avail) {
|
|
467
|
+
used += w;
|
|
468
|
+
fit++;
|
|
469
|
+
} else break;
|
|
470
|
+
}
|
|
471
|
+
if (fit >= chips.length) return; // everything fits
|
|
472
|
+
|
|
473
|
+
// Reserve space for the "+N" pill, then re-fit.
|
|
474
|
+
more.style.display = "";
|
|
475
|
+
more.textContent = `+${chips.length}`; // widest label, for measuring
|
|
476
|
+
const budget = avail - (more.offsetWidth + gap);
|
|
477
|
+
used = 0;
|
|
478
|
+
fit = 0;
|
|
479
|
+
for (const chip of chips) {
|
|
480
|
+
const w = chip.offsetWidth + gap;
|
|
481
|
+
if (used + w <= budget) {
|
|
482
|
+
used += w;
|
|
483
|
+
fit++;
|
|
484
|
+
} else break;
|
|
485
|
+
}
|
|
486
|
+
if (fit < 1) fit = 1;
|
|
487
|
+
for (const chip of chips.slice(fit)) chip.style.display = "none";
|
|
488
|
+
const hidden = chips.length - fit;
|
|
489
|
+
more.textContent = `+${hidden}`;
|
|
490
|
+
more.title = chips
|
|
491
|
+
.slice(fit)
|
|
492
|
+
.map((c) => c.textContent?.replace(/×$/, "").trim() ?? "")
|
|
493
|
+
.join(", ");
|
|
494
|
+
};
|
|
495
|
+
// Measure after layout settles.
|
|
496
|
+
const schedule = () =>
|
|
497
|
+
typeof requestAnimationFrame === "function"
|
|
498
|
+
? requestAnimationFrame(recompute)
|
|
499
|
+
: recompute();
|
|
500
|
+
schedule();
|
|
501
|
+
const ro =
|
|
502
|
+
typeof ResizeObserver !== "undefined"
|
|
503
|
+
? new ResizeObserver(schedule)
|
|
504
|
+
: null;
|
|
505
|
+
ro?.observe(node);
|
|
506
|
+
return {
|
|
507
|
+
update: schedule,
|
|
508
|
+
destroy: () => ro?.disconnect(),
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function onFilterChipKeydown(
|
|
513
|
+
event: KeyboardEvent,
|
|
514
|
+
columnId: string,
|
|
515
|
+
): void {
|
|
516
|
+
const input = event.currentTarget as HTMLInputElement;
|
|
517
|
+
if (event.key === "Enter" || event.key === ",") {
|
|
518
|
+
event.preventDefault();
|
|
519
|
+
commitFilterChip(columnId, input);
|
|
520
|
+
} else if (event.key === "Escape") {
|
|
521
|
+
closeInSuggest();
|
|
522
|
+
} else if (event.key === "Backspace" && input.value === "") {
|
|
523
|
+
const tokens = splitInTokens(filterRowValues[columnId] ?? "");
|
|
524
|
+
if (tokens.length) removeFilterChip(columnId, tokens[tokens.length - 1]!);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
371
527
|
</script>
|
|
372
528
|
|
|
373
529
|
<svelte:window
|
|
@@ -422,6 +578,53 @@
|
|
|
422
578
|
/>
|
|
423
579
|
</div>
|
|
424
580
|
</div>
|
|
581
|
+
{:else if schedulerConfig}
|
|
582
|
+
<div
|
|
583
|
+
class="sv-grid-root sv-grid-scheduler-root"
|
|
584
|
+
class:sv-grid-root-fill={props.containerHeight === "100%"}
|
|
585
|
+
style={`height: ${
|
|
586
|
+
typeof props.containerHeight === "string"
|
|
587
|
+
? props.containerHeight
|
|
588
|
+
: `${props.containerHeight ?? 520}px`
|
|
589
|
+
}; display: flex; flex-direction: column;`}
|
|
590
|
+
>
|
|
591
|
+
{#if schedulerConfig.searchable !== false}
|
|
592
|
+
<label class="sv-grid-board-search">
|
|
593
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" width="14" height="14">
|
|
594
|
+
<circle cx="7" cy="7" r="4.5" fill="none" stroke="currentColor" stroke-width="1.5" />
|
|
595
|
+
<line x1="10.2" y1="10.2" x2="14" y2="14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
596
|
+
</svg>
|
|
597
|
+
<input
|
|
598
|
+
type="search"
|
|
599
|
+
placeholder={schedulerConfig.searchPlaceholder ?? "Search events..."}
|
|
600
|
+
bind:value={ctrl.globalFilter}
|
|
601
|
+
aria-label="Search events"
|
|
602
|
+
/>
|
|
603
|
+
</label>
|
|
604
|
+
{/if}
|
|
605
|
+
<div style="flex: 1 1 auto; min-height: 0;">
|
|
606
|
+
{#if getSchedulerView()}
|
|
607
|
+
{@const SchedulerView = getSchedulerView()}
|
|
608
|
+
<SchedulerView
|
|
609
|
+
data={boardData}
|
|
610
|
+
columns={props.columns}
|
|
611
|
+
scheduler={schedulerConfig}
|
|
612
|
+
getRowId={props.getRowId}
|
|
613
|
+
/>
|
|
614
|
+
{:else}
|
|
615
|
+
<!-- The scheduler renderer ships in @svgrid/enterprise. Call
|
|
616
|
+
`enableSchedulerView()` (or `installEnterprise(api)`) to register it. -->
|
|
617
|
+
<div class="sv-grid-scheduler-upsell" role="note">
|
|
618
|
+
<strong>Scheduler view</strong>
|
|
619
|
+
<p>
|
|
620
|
+
The calendar / scheduler view is an Enterprise feature. Install
|
|
621
|
+
<code>@svgrid/enterprise</code> and call <code>enableSchedulerView()</code>
|
|
622
|
+
to render it.
|
|
623
|
+
</p>
|
|
624
|
+
</div>
|
|
625
|
+
{/if}
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
425
628
|
{:else}
|
|
426
629
|
{#snippet icon(name: string)}
|
|
427
630
|
<svg
|
|
@@ -473,6 +676,34 @@
|
|
|
473
676
|
{:else if name === "op-isBlank"}
|
|
474
677
|
<circle cx="12" cy="12" r="8" />
|
|
475
678
|
<path d="M6.5 6.5l11 11" />
|
|
679
|
+
{:else if name === "op-isNotBlank"}
|
|
680
|
+
<circle cx="12" cy="12" r="8" />
|
|
681
|
+
<circle cx="12" cy="12" r="3.2" fill="currentColor" stroke="none" />
|
|
682
|
+
{:else if name === "op-notContains"}
|
|
683
|
+
<circle cx="11" cy="11" r="6" />
|
|
684
|
+
<path d="M20 20l-4.5-4.5" />
|
|
685
|
+
<path d="M7 11h8" />
|
|
686
|
+
{:else if name === "op-notEquals"}
|
|
687
|
+
<path d="M5 9.5h14" />
|
|
688
|
+
<path d="M5 14.5h14" />
|
|
689
|
+
<path d="M16 5l-8 14" />
|
|
690
|
+
{:else if name === "op-endsWith"}
|
|
691
|
+
<path d="M19 5v14" />
|
|
692
|
+
<path d="M15 9H5" />
|
|
693
|
+
<path d="M15 15H8" />
|
|
694
|
+
{:else if name === "op-regex"}
|
|
695
|
+
<path d="M12 5v9" />
|
|
696
|
+
<path d="M8.1 7.4l7.8 4.5" />
|
|
697
|
+
<path d="M15.9 7.4l-7.8 4.5" />
|
|
698
|
+
<circle cx="6" cy="18" r="1.4" fill="currentColor" stroke="none" />
|
|
699
|
+
{:else if name === "op-in"}
|
|
700
|
+
<path d="M14 5a7 7 0 1 0 0 14" />
|
|
701
|
+
<path d="M6 12h9" />
|
|
702
|
+
<path d="M12 9l3 3-3 3" />
|
|
703
|
+
{:else if name === "op-notIn"}
|
|
704
|
+
<path d="M14 5a7 7 0 1 0 0 14" />
|
|
705
|
+
<path d="M15 12H6" />
|
|
706
|
+
<path d="M9 9l-3 3 3 3" />
|
|
476
707
|
{:else if name === "autosize"}
|
|
477
708
|
<path d="M3 12h18" />
|
|
478
709
|
<path d="M3 12l4-4" />
|
|
@@ -645,10 +876,18 @@
|
|
|
645
876
|
{#if cf.dataBar}
|
|
646
877
|
<div
|
|
647
878
|
class="sv-grid-cf-bar"
|
|
648
|
-
style={`width:${cf.dataBar.percent}%;background:${
|
|
879
|
+
style={`width:${cf.dataBar.percent}%;background:${
|
|
880
|
+
cf.dataBar.gradient
|
|
881
|
+
? `linear-gradient(90deg, color-mix(in srgb, ${cf.dataBar.color} 35%, transparent), ${cf.dataBar.color})`
|
|
882
|
+
: cf.dataBar.color
|
|
883
|
+
}`}
|
|
649
884
|
></div>
|
|
650
885
|
{/if}
|
|
651
|
-
<span
|
|
886
|
+
<span
|
|
887
|
+
class="sv-grid-cf-content"
|
|
888
|
+
style={cfTextStyle(cf)}
|
|
889
|
+
title={cf.title ?? undefined}
|
|
890
|
+
>
|
|
652
891
|
{#if cf.icon}<span class="sv-grid-cf-icon">{cf.icon}</span>{/if}
|
|
653
892
|
{#if !cf.iconOnly}{@render cellBody(row, column, cellValue)}{/if}
|
|
654
893
|
</span>
|
|
@@ -914,6 +1153,7 @@
|
|
|
914
1153
|
formatString="yyyy-MM-dd"
|
|
915
1154
|
dropDownDisplayMode="calendar"
|
|
916
1155
|
autoOpen
|
|
1156
|
+
block
|
|
917
1157
|
onChange={(d) => updateEditingCellValue(d)}
|
|
918
1158
|
onCommit={() => saveEditingCell()}
|
|
919
1159
|
onCancel={() => {
|
|
@@ -927,6 +1167,7 @@
|
|
|
927
1167
|
formatString="yyyy-MM-dd HH:mm"
|
|
928
1168
|
dropDownDisplayMode="both"
|
|
929
1169
|
autoOpen
|
|
1170
|
+
block
|
|
930
1171
|
onChange={(d) => updateEditingCellValue(d)}
|
|
931
1172
|
onCommit={() => saveEditingCell()}
|
|
932
1173
|
onCancel={() => {
|
|
@@ -942,6 +1183,7 @@
|
|
|
942
1183
|
formatString="HH:mm"
|
|
943
1184
|
dropDownDisplayMode="time"
|
|
944
1185
|
autoOpen
|
|
1186
|
+
block
|
|
945
1187
|
onChange={(d) => updateEditingCellValue(dateToTimeString(d))}
|
|
946
1188
|
onCommit={() => saveEditingCell()}
|
|
947
1189
|
onCancel={() => {
|
|
@@ -1368,6 +1610,7 @@
|
|
|
1368
1610
|
<div
|
|
1369
1611
|
class="sv-grid-container sv-grid-container-custom-scrollbars"
|
|
1370
1612
|
class:sv-grid-has-vscroll={hasMeasured && hasVerticalOverflow}
|
|
1613
|
+
class:sv-grid-has-hscroll={hasMeasured && hasHorizontalOverflow}
|
|
1371
1614
|
class:sv-grid-narrow={ctrl.isNarrowResponsive}
|
|
1372
1615
|
bind:this={ctrl.scrollContainer}
|
|
1373
1616
|
onscroll={onBodyScroll}
|
|
@@ -1415,6 +1658,13 @@
|
|
|
1415
1658
|
aria-hidden="true"
|
|
1416
1659
|
></th>
|
|
1417
1660
|
{/if}
|
|
1661
|
+
{#if columnVirtualizationEnabled && columnWindowStart > 0}
|
|
1662
|
+
<th
|
|
1663
|
+
class="sv-grid-column sv-grid-column-spacer"
|
|
1664
|
+
aria-hidden="true"
|
|
1665
|
+
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
1666
|
+
></th>
|
|
1667
|
+
{/if}
|
|
1418
1668
|
{#each row.cells as cell (cell.key)}
|
|
1419
1669
|
<th
|
|
1420
1670
|
class="sv-grid-column sv-grid-group-header-cell"
|
|
@@ -1463,6 +1713,13 @@
|
|
|
1463
1713
|
{/if}
|
|
1464
1714
|
</th>
|
|
1465
1715
|
{/each}
|
|
1716
|
+
{#if columnVirtualizationEnabled && columnWindowRightSpacer > 0}
|
|
1717
|
+
<th
|
|
1718
|
+
class="sv-grid-column sv-grid-column-spacer"
|
|
1719
|
+
aria-hidden="true"
|
|
1720
|
+
style={`width: ${columnWindowRightSpacer}px; min-width: ${columnWindowRightSpacer}px; max-width: ${columnWindowRightSpacer}px;`}
|
|
1721
|
+
></th>
|
|
1722
|
+
{/if}
|
|
1466
1723
|
</tr>
|
|
1467
1724
|
{/each}
|
|
1468
1725
|
{#each headerGroups as headerGroup (headerGroup.id)}
|
|
@@ -1789,18 +2046,84 @@
|
|
|
1789
2046
|
>{@render icon("chevron-down")}</span
|
|
1790
2047
|
>
|
|
1791
2048
|
</button>
|
|
1792
|
-
{#if activeOperator
|
|
2049
|
+
{#if activeOperator === "in" || activeOperator === "notIn"}
|
|
2050
|
+
{@const frTokens = splitInTokens(
|
|
2051
|
+
filterRowValues[rendered.column.id] ?? "",
|
|
2052
|
+
)}
|
|
2053
|
+
<div
|
|
2054
|
+
class="sv-grid-filter-chips"
|
|
2055
|
+
use:fitChips={`${frTokens.length}:${rendered.item.size}`}
|
|
2056
|
+
>
|
|
2057
|
+
{#each frTokens as token (token)}
|
|
2058
|
+
<span class="sv-grid-filter-chip" data-chip>
|
|
2059
|
+
<span class="sv-grid-filter-chip-label"
|
|
2060
|
+
>{token}</span
|
|
2061
|
+
>
|
|
2062
|
+
<button
|
|
2063
|
+
type="button"
|
|
2064
|
+
class="sv-grid-filter-chip-x"
|
|
2065
|
+
aria-label={`Remove ${token}`}
|
|
2066
|
+
onmousedown={(event) => {
|
|
2067
|
+
event.preventDefault();
|
|
2068
|
+
removeFilterChip(rendered.column.id, token);
|
|
2069
|
+
}}>×</button
|
|
2070
|
+
>
|
|
2071
|
+
</span>
|
|
2072
|
+
{/each}
|
|
2073
|
+
<!-- Overflow pill: the fitChips action shows/labels
|
|
2074
|
+
it when chips don't fit; click opens the list. -->
|
|
2075
|
+
<button
|
|
2076
|
+
type="button"
|
|
2077
|
+
class="sv-grid-filter-chip sv-grid-filter-chip-more"
|
|
2078
|
+
data-more
|
|
2079
|
+
style="display: none;"
|
|
2080
|
+
aria-label="More selected values. Open value list"
|
|
2081
|
+
onclick={(event) => {
|
|
2082
|
+
(
|
|
2083
|
+
event.currentTarget as HTMLElement
|
|
2084
|
+
).parentElement
|
|
2085
|
+
?.querySelector<HTMLInputElement>("input")
|
|
2086
|
+
?.focus();
|
|
2087
|
+
}}
|
|
2088
|
+
></button>
|
|
2089
|
+
<input
|
|
2090
|
+
class="sv-grid-filter-value sv-grid-filter-chip-input"
|
|
2091
|
+
type="text"
|
|
2092
|
+
placeholder={frTokens.length ? "" : "Add value…"}
|
|
2093
|
+
data-svgrid-filter-col={rendered.column.id}
|
|
2094
|
+
onfocus={(event) =>
|
|
2095
|
+
onFilterChipFocus(event, rendered.column.id)}
|
|
2096
|
+
oninput={(event) =>
|
|
2097
|
+
onFilterChipInput(event, rendered.column.id)}
|
|
2098
|
+
onkeydown={(event) =>
|
|
2099
|
+
onFilterChipKeydown(event, rendered.column.id)}
|
|
2100
|
+
onblur={(event) => {
|
|
2101
|
+
commitFilterChip(
|
|
2102
|
+
rendered.column.id,
|
|
2103
|
+
event.currentTarget as HTMLInputElement,
|
|
2104
|
+
);
|
|
2105
|
+
closeInSuggest();
|
|
2106
|
+
}}
|
|
2107
|
+
/>
|
|
2108
|
+
</div>
|
|
2109
|
+
{:else if activeOperator !== "isBlank" && activeOperator !== "isNotBlank"}
|
|
1793
2110
|
{@const frType = getEditorInputType(
|
|
1794
2111
|
rendered.column.columnDef.editorType ?? "text",
|
|
1795
2112
|
)}
|
|
2113
|
+
{@const frValue =
|
|
2114
|
+
filterRowValues[rendered.column.id] ?? ""}
|
|
1796
2115
|
<input
|
|
1797
2116
|
class="sv-grid-filter-value"
|
|
1798
|
-
|
|
2117
|
+
class:sv-grid-filter-value-invalid={activeOperator ===
|
|
2118
|
+
"regex" && isInvalidRegex(frValue)}
|
|
2119
|
+
type={activeOperator === "regex" ? "text" : frType}
|
|
1799
2120
|
placeholder={activeOperator === "between"
|
|
1800
2121
|
? "From"
|
|
1801
|
-
: "
|
|
2122
|
+
: activeOperator === "regex"
|
|
2123
|
+
? "Pattern…"
|
|
2124
|
+
: "Filter…"}
|
|
1802
2125
|
data-svgrid-filter-col={rendered.column.id}
|
|
1803
|
-
value={
|
|
2126
|
+
value={frValue}
|
|
1804
2127
|
oninput={(event) =>
|
|
1805
2128
|
updateFilterRow(
|
|
1806
2129
|
rendered.column.id,
|
|
@@ -2145,6 +2468,7 @@
|
|
|
2145
2468
|
aria-level={row.depth + 1}
|
|
2146
2469
|
aria-expanded={row.getIsExpanded?.() ? "true" : "false"}
|
|
2147
2470
|
{...getGridRowA11yProps(rowIndex + 1)}
|
|
2471
|
+
style={`height: ${rowSizePx(rowIndex)}px;`}
|
|
2148
2472
|
>
|
|
2149
2473
|
<td
|
|
2150
2474
|
class="sv-grid-cell sv-grid-group-cell"
|
|
@@ -2170,6 +2494,7 @@
|
|
|
2170
2494
|
aria-level={sgAriaLevel(row)}
|
|
2171
2495
|
aria-expanded={sgAriaExpanded(row)}
|
|
2172
2496
|
{...rowDragAttrs(rowIndex)}
|
|
2497
|
+
style={`height: ${rowSizePx(rowIndex)}px;`}
|
|
2173
2498
|
>
|
|
2174
2499
|
{#if showRowNumbersEffective}
|
|
2175
2500
|
<td
|
|
@@ -2255,6 +2580,7 @@
|
|
|
2255
2580
|
data-svgrid-row={rowIndex}
|
|
2256
2581
|
data-svgrid-col={colIndex}
|
|
2257
2582
|
data-col-id={rendered.column.id}
|
|
2583
|
+
data-align={getColumnAlign(rendered.column)}
|
|
2258
2584
|
data-pinned={isColumnPinned(rendered.column.id) ??
|
|
2259
2585
|
undefined}
|
|
2260
2586
|
data-selected-range={rangeEdges ? "true" : undefined}
|
|
@@ -2547,6 +2873,8 @@
|
|
|
2547
2873
|
event.preventDefault();
|
|
2548
2874
|
ctrl.findOpen = false;
|
|
2549
2875
|
ctrl.findQuery = "";
|
|
2876
|
+
// Return focus to the grid instead of letting it fall to <body> (#80).
|
|
2877
|
+
ctrl.gridRootEl?.focus({ preventScroll: true });
|
|
2550
2878
|
}
|
|
2551
2879
|
}}
|
|
2552
2880
|
/>
|
|
@@ -2593,6 +2921,8 @@
|
|
|
2593
2921
|
onclick={() => {
|
|
2594
2922
|
ctrl.findOpen = false;
|
|
2595
2923
|
ctrl.findQuery = "";
|
|
2924
|
+
// Return focus to the grid instead of letting it fall to <body> (#80).
|
|
2925
|
+
ctrl.gridRootEl?.focus({ preventScroll: true });
|
|
2596
2926
|
}}>✕</button
|
|
2597
2927
|
>
|
|
2598
2928
|
</div>
|
|
@@ -2741,11 +3071,19 @@
|
|
|
2741
3071
|
>
|
|
2742
3072
|
{/each}
|
|
2743
3073
|
</select>
|
|
2744
|
-
{#if active !== "isBlank"}
|
|
3074
|
+
{#if active !== "isBlank" && active !== "isNotBlank"}
|
|
3075
|
+
{@const tpIsSetOp = active === "in" || active === "notIn"}
|
|
2745
3076
|
<input
|
|
2746
3077
|
class="sv-grid-tp-filter-input"
|
|
2747
|
-
type={fType}
|
|
2748
|
-
|
|
3078
|
+
type={active === "regex" ? "text" : fType}
|
|
3079
|
+
list={tpIsSetOp ? `sv-tp-in-list-${column.id}` : undefined}
|
|
3080
|
+
placeholder={active === "between"
|
|
3081
|
+
? "From"
|
|
3082
|
+
: tpIsSetOp
|
|
3083
|
+
? "value, value…"
|
|
3084
|
+
: active === "regex"
|
|
3085
|
+
? "Pattern…"
|
|
3086
|
+
: "Filter…"}
|
|
2749
3087
|
value={filterMenuValues[column.id]?.value ?? ""}
|
|
2750
3088
|
oninput={(e) =>
|
|
2751
3089
|
updateFilterMenuValue(
|
|
@@ -2753,6 +3091,13 @@
|
|
|
2753
3091
|
e.currentTarget.value,
|
|
2754
3092
|
)}
|
|
2755
3093
|
/>
|
|
3094
|
+
{#if tpIsSetOp}
|
|
3095
|
+
<datalist id={`sv-tp-in-list-${column.id}`}>
|
|
3096
|
+
{#each facetValuesForColumn(column.id).slice(0, 200) as v (v)}
|
|
3097
|
+
<option value={v}></option>
|
|
3098
|
+
{/each}
|
|
3099
|
+
</datalist>
|
|
3100
|
+
{/if}
|
|
2756
3101
|
{#if active === "between"}
|
|
2757
3102
|
<input
|
|
2758
3103
|
class="sv-grid-tp-filter-input"
|