@svgrid/enterprise 3.1.7 → 3.1.9
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 +1 -0
- package/dist/SvAlertRuleEditor.svelte +16 -5
- package/dist/SvGridAlerts.svelte +8 -0
- package/dist/SvGridAlerts.svelte.d.ts +4 -0
- package/dist/SvGridBoard.svelte +69 -10
- package/dist/SvGridBulkEditDrawer.svelte +13 -5
- package/dist/SvGridGallery.svelte +286 -0
- package/dist/SvGridGallery.svelte.d.ts +30 -0
- package/dist/SvGridScheduler.svelte +33 -11
- package/dist/SvGridSelectionBar.svelte +47 -0
- package/dist/advanced-filter/expr-columns-from-grid.d.ts +1 -0
- package/dist/advanced-filter/expr-columns-from-grid.js +11 -7
- package/dist/advanced-filter-enable.d.ts +6 -0
- package/dist/advanced-filter-enable.js +8 -0
- package/dist/alerts/alert-engine.d.ts +7 -0
- package/dist/alerts/alert-engine.js +5 -2
- package/dist/alerts/condition-editor.d.ts +26 -0
- package/dist/alerts/condition-editor.js +17 -0
- package/dist/base/index.d.ts +20 -0
- package/dist/base/index.js +20 -0
- package/dist/bulk-edit.d.ts +11 -0
- package/dist/bulk-edit.js +18 -1
- package/dist/cdn/svgrid-enterprise.svelte-external.js +11375 -10805
- package/dist/designer/assets/GridFooter-Cu8_yBlQ.js +2 -0
- package/dist/designer/assets/GridMenus-BUjkkAU2.js +6 -0
- package/dist/designer/assets/PinSplitter-BgTpgh1d.css +1 -0
- package/dist/designer/assets/PinSplitter-C5AwA1xV.js +1 -0
- package/dist/designer/assets/SvGrid.helpers-CIrmIo9U.js +1 -0
- package/dist/designer/assets/SvGridCellEditor-CJxThKL_.js +4 -0
- package/dist/designer/assets/SvListBox-CS7oUAiX.js +2 -0
- package/dist/designer/assets/conditional-formatting-MDWbF8_Y.js +1 -0
- package/dist/designer/assets/grid-extension.svelte-BZ7AqyJI.js +1 -0
- package/dist/designer/assets/{index-DtBIaQno.js → index-BPU2KNc5.js} +396 -395
- package/dist/designer/assets/index-BmvG99kR.css +1 -0
- package/dist/designer/assets/sparkline-C-NcnB2i.js +1 -0
- package/dist/designer/assets/src-C_U1NKOY.js +58 -0
- package/dist/designer/assets/sv-grid-scrollbar-B9N6Wuj0.js +109 -0
- package/dist/designer/assets/validate-H5u2HEQh.js +1 -0
- package/dist/designer/index.html +11 -7
- package/dist/expressions/compile.js +10 -0
- package/dist/expressions/evaluate.js +18 -0
- package/dist/expressions/expression-types.d.ts +10 -0
- package/dist/expressions/kinds.d.ts +28 -0
- package/dist/expressions/kinds.js +19 -0
- package/dist/expressions/parse.js +9 -1
- package/dist/fields/AttachmentCell.svelte +242 -0
- package/dist/fields/AttachmentCell.svelte.d.ts +5 -0
- package/dist/fields/AttachmentDialog.svelte +357 -0
- package/dist/fields/AttachmentDialog.svelte.d.ts +13 -0
- package/dist/fields/AttachmentEditor.svelte +41 -0
- package/dist/fields/AttachmentEditor.svelte.d.ts +11 -0
- package/dist/fields/AttachmentPreview.svelte +72 -0
- package/dist/fields/AttachmentPreview.svelte.d.ts +8 -0
- package/dist/fields/BarcodeCell.svelte +20 -0
- package/dist/fields/BarcodeCell.svelte.d.ts +5 -0
- package/dist/fields/BaseAutomations.svelte +558 -0
- package/dist/fields/BaseAutomations.svelte.d.ts +18 -0
- package/dist/fields/BaseChartView.svelte +47 -0
- package/dist/fields/BaseChartView.svelte.d.ts +8 -0
- package/dist/fields/BaseTableView.svelte +185 -0
- package/dist/fields/BaseTableView.svelte.d.ts +37 -0
- package/dist/fields/BaseTabs.svelte +308 -0
- package/dist/fields/BaseTabs.svelte.d.ts +21 -0
- package/dist/fields/ButtonCell.svelte +47 -0
- package/dist/fields/ButtonCell.svelte.d.ts +20 -0
- package/dist/fields/CheckboxCell.svelte +46 -0
- package/dist/fields/CheckboxCell.svelte.d.ts +14 -0
- package/dist/fields/DeleteFieldConfirm.svelte +80 -0
- package/dist/fields/DeleteFieldConfirm.svelte.d.ts +8 -0
- package/dist/fields/DeleteRecordConfirm.svelte +48 -0
- package/dist/fields/DeleteRecordConfirm.svelte.d.ts +9 -0
- package/dist/fields/FieldCell.svelte +80 -0
- package/dist/fields/FieldCell.svelte.d.ts +9 -0
- package/dist/fields/FieldEditor.svelte +321 -0
- package/dist/fields/FieldEditor.svelte.d.ts +10 -0
- package/dist/fields/FieldInput.svelte +188 -0
- package/dist/fields/FieldInput.svelte.d.ts +29 -0
- package/dist/fields/FieldsHeaderEnd.svelte +146 -0
- package/dist/fields/FieldsHeaderEnd.svelte.d.ts +6 -0
- package/dist/fields/FootRowAdder.svelte +8 -0
- package/dist/fields/FootRowAdder.svelte.d.ts +6 -0
- package/dist/fields/GroupHeader.svelte +127 -0
- package/dist/fields/GroupHeader.svelte.d.ts +24 -0
- package/dist/fields/LinkCell.svelte +46 -0
- package/dist/fields/LinkCell.svelte.d.ts +9 -0
- package/dist/fields/LinkPickerEditor.svelte +172 -0
- package/dist/fields/LinkPickerEditor.svelte.d.ts +9 -0
- package/dist/fields/LinkPreview.svelte +60 -0
- package/dist/fields/LinkPreview.svelte.d.ts +7 -0
- package/dist/fields/LinkedRecordView.svelte +94 -0
- package/dist/fields/LinkedRecordView.svelte.d.ts +9 -0
- package/dist/fields/LinkedRecordsCell.svelte +59 -0
- package/dist/fields/LinkedRecordsCell.svelte.d.ts +11 -0
- package/dist/fields/LongTextEditor.svelte +157 -0
- package/dist/fields/LongTextEditor.svelte.d.ts +11 -0
- package/dist/fields/NumberEditor.svelte +309 -0
- package/dist/fields/NumberEditor.svelte.d.ts +12 -0
- package/dist/fields/OptionsEditor.svelte +117 -0
- package/dist/fields/OptionsEditor.svelte.d.ts +11 -0
- package/dist/fields/PercentCell.svelte +21 -0
- package/dist/fields/PercentCell.svelte.d.ts +8 -0
- package/dist/fields/PropertyInput.svelte +204 -0
- package/dist/fields/PropertyInput.svelte.d.ts +11 -0
- package/dist/fields/RatingCell.svelte +75 -0
- package/dist/fields/RatingCell.svelte.d.ts +16 -0
- package/dist/fields/RatingEditor.svelte +82 -0
- package/dist/fields/RatingEditor.svelte.d.ts +9 -0
- package/dist/fields/RecordView.svelte +310 -0
- package/dist/fields/RecordView.svelte.d.ts +11 -0
- package/dist/fields/RichTextCell.svelte +30 -0
- package/dist/fields/RichTextCell.svelte.d.ts +5 -0
- package/dist/fields/RowAdder.svelte +93 -0
- package/dist/fields/RowAdder.svelte.d.ts +7 -0
- package/dist/fields/RowHeader.svelte +99 -0
- package/dist/fields/RowHeader.svelte.d.ts +11 -0
- package/dist/fields/SelectCell.svelte +35 -0
- package/dist/fields/SelectCell.svelte.d.ts +15 -0
- package/dist/fields/SelectEditor.svelte +350 -0
- package/dist/fields/SelectEditor.svelte.d.ts +17 -0
- package/dist/fields/SummaryCell.svelte +160 -0
- package/dist/fields/SummaryCell.svelte.d.ts +11 -0
- package/dist/fields/SvBase.svelte +545 -0
- package/dist/fields/SvBase.svelte.d.ts +30 -0
- package/dist/fields/SvBaseForm.svelte +295 -0
- package/dist/fields/SvBaseForm.svelte.d.ts +22 -0
- package/dist/fields/SvBaseViewBar.svelte +644 -0
- package/dist/fields/SvBaseViewBar.svelte.d.ts +26 -0
- package/dist/fields/UserCell.svelte +39 -0
- package/dist/fields/UserCell.svelte.d.ts +9 -0
- package/dist/fields/UserFace.svelte +30 -0
- package/dist/fields/UserFace.svelte.d.ts +12 -0
- package/dist/fields/activity.svelte.d.ts +50 -0
- package/dist/fields/activity.svelte.js +110 -0
- package/dist/fields/attachments.d.ts +95 -0
- package/dist/fields/attachments.js +145 -0
- package/dist/fields/automation-outbound.d.ts +46 -0
- package/dist/fields/automation-outbound.js +61 -0
- package/dist/fields/automation-server.d.ts +53 -0
- package/dist/fields/automation-server.js +333 -0
- package/dist/fields/automations.svelte.d.ts +208 -0
- package/dist/fields/automations.svelte.js +437 -0
- package/dist/fields/bar-request.svelte.d.ts +16 -0
- package/dist/fields/bar-request.svelte.js +15 -0
- package/dist/fields/base-alerts.d.ts +24 -0
- package/dist/fields/base-alerts.js +64 -0
- package/dist/fields/base-connect.d.ts +82 -0
- package/dist/fields/base-connect.js +271 -0
- package/dist/fields/base-schema.d.ts +104 -0
- package/dist/fields/base-schema.js +266 -0
- package/dist/fields/base-sql.d.ts +62 -0
- package/dist/fields/base-sql.js +251 -0
- package/dist/fields/base-sync.d.ts +142 -0
- package/dist/fields/base-sync.js +499 -0
- package/dist/fields/base-tables.d.ts +89 -0
- package/dist/fields/base-tables.js +349 -0
- package/dist/fields/builtin-types.d.ts +13 -0
- package/dist/fields/builtin-types.js +773 -0
- package/dist/fields/button.d.ts +2 -0
- package/dist/fields/button.js +17 -0
- package/dist/fields/chart-view.d.ts +35 -0
- package/dist/fields/chart-view.js +154 -0
- package/dist/fields/column-order.d.ts +6 -0
- package/dist/fields/column-order.js +12 -0
- package/dist/fields/convert.d.ts +16 -0
- package/dist/fields/convert.js +176 -0
- package/dist/fields/copy-text.d.ts +13 -0
- package/dist/fields/copy-text.js +45 -0
- package/dist/fields/editor-request.svelte.d.ts +71 -0
- package/dist/fields/editor-request.svelte.js +52 -0
- package/dist/fields/field-ops.d.ts +72 -0
- package/dist/fields/field-ops.js +334 -0
- package/dist/fields/filter-model.d.ts +84 -0
- package/dist/fields/filter-model.js +446 -0
- package/dist/fields/format.d.ts +109 -0
- package/dist/fields/format.js +288 -0
- package/dist/fields/formula.d.ts +50 -0
- package/dist/fields/formula.js +530 -0
- package/dist/fields/grid-access.d.ts +60 -0
- package/dist/fields/grid-access.js +120 -0
- package/dist/fields/index.d.ts +34 -0
- package/dist/fields/index.js +312 -0
- package/dist/fields/linked-fields.d.ts +8 -0
- package/dist/fields/linked-fields.js +40 -0
- package/dist/fields/links.d.ts +10 -0
- package/dist/fields/links.js +46 -0
- package/dist/fields/name-list.d.ts +16 -0
- package/dist/fields/name-list.js +49 -0
- package/dist/fields/palette.d.ts +47 -0
- package/dist/fields/palette.js +69 -0
- package/dist/fields/record-delete.svelte.d.ts +12 -0
- package/dist/fields/record-delete.svelte.js +5 -0
- package/dist/fields/record-menu.d.ts +9 -0
- package/dist/fields/record-menu.js +78 -0
- package/dist/fields/registry.svelte.d.ts +176 -0
- package/dist/fields/registry.svelte.js +51 -0
- package/dist/fields/rich-text.d.ts +23 -0
- package/dist/fields/rich-text.js +48 -0
- package/dist/fields/row-color-choice.svelte.d.ts +31 -0
- package/dist/fields/row-color-choice.svelte.js +25 -0
- package/dist/fields/row-colors.d.ts +36 -0
- package/dist/fields/row-colors.js +90 -0
- package/dist/fields/row-height.d.ts +28 -0
- package/dist/fields/row-height.js +50 -0
- package/dist/fields/sources.svelte.d.ts +80 -0
- package/dist/fields/sources.svelte.js +131 -0
- package/dist/fields/summaries.d.ts +18 -0
- package/dist/fields/summaries.js +138 -0
- package/dist/fields/view-model.d.ts +129 -0
- package/dist/fields/view-model.js +492 -0
- package/dist/fields/viewbar/ChartPanel.svelte +61 -0
- package/dist/fields/viewbar/ChartPanel.svelte.d.ts +9 -0
- package/dist/fields/viewbar/ColorPanel.svelte +112 -0
- package/dist/fields/viewbar/ColorPanel.svelte.d.ts +9 -0
- package/dist/fields/viewbar/ConditionValue.svelte +153 -0
- package/dist/fields/viewbar/ConditionValue.svelte.d.ts +15 -0
- package/dist/fields/viewbar/FieldIcon.svelte +18 -0
- package/dist/fields/viewbar/FieldIcon.svelte.d.ts +9 -0
- package/dist/fields/viewbar/FilterPanel.svelte +192 -0
- package/dist/fields/viewbar/FilterPanel.svelte.d.ts +13 -0
- package/dist/fields/viewbar/GanttPanel.svelte +60 -0
- package/dist/fields/viewbar/GanttPanel.svelte.d.ts +10 -0
- package/dist/fields/viewbar/HideFieldsPanel.svelte +104 -0
- package/dist/fields/viewbar/HideFieldsPanel.svelte.d.ts +7 -0
- package/dist/fields/viewbar/SortPanel.svelte +85 -0
- package/dist/fields/viewbar/SortPanel.svelte.d.ts +8 -0
- package/dist/fields/viewbar/ViewsMenu.svelte +250 -0
- package/dist/fields/viewbar/ViewsMenu.svelte.d.ts +15 -0
- package/dist/fields/viewbar/panel-focus.d.ts +3 -0
- package/dist/fields/viewbar/panel-focus.js +22 -0
- package/dist/fields/viewbar/sort-state.d.ts +20 -0
- package/dist/fields/viewbar/sort-state.js +24 -0
- package/dist/gallery.d.ts +7 -0
- package/dist/gallery.js +36 -0
- package/dist/gantt/SvGridGantt.svelte +88 -16
- package/dist/gantt/gantt-config.d.ts +28 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/install.js +3 -0
- package/dist/license-core.d.ts +3 -3
- package/dist/license.js +1 -0
- package/dist/node/studio.js +141 -20
- package/dist/scheduler-config.d.ts +11 -0
- package/dist/sources/introspect-supabase.js +4 -1
- package/dist/sources/rest.js +6 -2
- package/dist/studio/emit-project.js +25 -6
- package/dist/studio/sveltekit3.js +13 -7
- package/dist/studio/ui-components.generated.js +94 -2
- package/dist/upgrade-prompt.d.ts +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/wc/GridFooter-f5K0aLe_.js +145 -0
- package/dist/wc/GridMenus-FtSN_8t9.js +1369 -0
- package/dist/wc/PinSplitter-D-qtDhSM.js +132 -0
- package/dist/wc/{SvGrid.helpers-zBkDTryR.js → SvGrid.helpers-B2GEKrxS.js} +84 -36
- package/dist/wc/{SvGridCellEditor-CzUKEbkI.js → SvGridCellEditor-B_cLJrsp.js} +20 -9
- package/dist/wc/conditional-formatting-BXis_oP1.js +189 -0
- package/dist/wc/grid-extension.svelte-Bufq4B4C.js +20 -0
- package/dist/wc/sv-sheet-element.js +7561 -7618
- package/dist/wc/svelte-virtualizer.svelte-ehwOupl-.js +360 -0
- package/dist/wc/{validate-43C03DLP.js → validate-BaMO8UPE.js} +1 -1
- package/package.json +14 -3
- package/dist/designer/assets/GridMenus-CvcASlKv.js +0 -8
- package/dist/designer/assets/SvGrid.helpers-DE5mmORW.js +0 -1
- package/dist/designer/assets/SvGridCellEditor-C7NWcnNC.js +0 -4
- package/dist/designer/assets/SvListBox-BlmmzYcU.js +0 -110
- package/dist/designer/assets/index-KmSEY7Lb.css +0 -1
- package/dist/designer/assets/src-DqHCqori.js +0 -58
- package/dist/designer/assets/validate-B71dE1Cs.js +0 -1
- package/dist/wc/GridMenus-fWbBUiRH.js +0 -1088
- package/dist/wc/svelte-virtualizer.svelte-Dntdh8Ms.js +0 -581
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* The view bar's "Hide fields": a switch per field, a search, hide all /
|
|
4
|
+
* show all, and drag to reorder. The primary field is always shown.
|
|
5
|
+
*/
|
|
6
|
+
import { panelFocus } from './panel-focus'
|
|
7
|
+
import { fieldColumns, primaryIdOf, type FieldColumn } from '../grid-access'
|
|
8
|
+
import { orderKeepingScreen } from '../column-order'
|
|
9
|
+
import FieldIcon from './FieldIcon.svelte'
|
|
10
|
+
|
|
11
|
+
let { ctrl, api }: { ctrl: any; api: any } = $props()
|
|
12
|
+
|
|
13
|
+
let query = $state('')
|
|
14
|
+
const primary = $derived(primaryIdOf(ctrl))
|
|
15
|
+
const columns = $derived(fieldColumns(ctrl))
|
|
16
|
+
const shown = $derived(columns.filter((c) => !query.trim() || c.header.toLowerCase().includes(query.trim().toLowerCase())))
|
|
17
|
+
|
|
18
|
+
function setVisible(c: FieldColumn, visible: boolean) {
|
|
19
|
+
if (c.id === primary && !visible) return
|
|
20
|
+
api.setColumnVisible(c.id, visible)
|
|
21
|
+
}
|
|
22
|
+
function setAll(visible: boolean) {
|
|
23
|
+
for (const c of columns) if (c.id !== primary) api.setColumnVisible(c.id, visible)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Drag to reorder: the new order is the list's, with columns not in it kept in their slots.
|
|
27
|
+
let dragId = $state<string | null>(null)
|
|
28
|
+
let overId = $state<string | null>(null)
|
|
29
|
+
function drop(targetId: string) {
|
|
30
|
+
const from = dragId
|
|
31
|
+
dragId = overId = null
|
|
32
|
+
if (!from || from === targetId || from === primary || targetId === primary) return
|
|
33
|
+
const ids = columns.map((c) => c.id)
|
|
34
|
+
const without = ids.filter((id) => id !== from)
|
|
35
|
+
without.splice(without.indexOf(targetId) + (ids.indexOf(from) < ids.indexOf(targetId) ? 1 : 0), 0, from)
|
|
36
|
+
const all: string[] = ctrl.grid.getAllColumns().map((c: { id: string }) => c.id)
|
|
37
|
+
const user: string[] = ctrl.userColumnOrder ?? []
|
|
38
|
+
const base = user.length ? [...user, ...all.filter((id) => !user.includes(id))] : all
|
|
39
|
+
ctrl.setColumnOrderInternal(orderKeepingScreen(base, without))
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<div class="sv-fvb-panel sv-fhide" use:panelFocus>
|
|
44
|
+
<input class="sv-fvb-input" type="search" placeholder="Find a field" aria-label="Find a field" bind:value={query} />
|
|
45
|
+
<ul class="sv-fhide-list" role="list">
|
|
46
|
+
{#each shown as c (c.id)}
|
|
47
|
+
<li
|
|
48
|
+
class="sv-fhide-item"
|
|
49
|
+
class:is-over={overId === c.id && dragId !== c.id}
|
|
50
|
+
draggable={c.id !== primary && !query}
|
|
51
|
+
ondragstart={(e) => {
|
|
52
|
+
dragId = c.id
|
|
53
|
+
e.dataTransfer?.setData('text/plain', c.id)
|
|
54
|
+
}}
|
|
55
|
+
ondragover={(e) => {
|
|
56
|
+
if (!dragId) return
|
|
57
|
+
e.preventDefault()
|
|
58
|
+
overId = c.id
|
|
59
|
+
}}
|
|
60
|
+
ondragleave={() => {
|
|
61
|
+
if (overId === c.id) overId = null
|
|
62
|
+
}}
|
|
63
|
+
ondrop={(e) => {
|
|
64
|
+
e.preventDefault()
|
|
65
|
+
drop(c.id)
|
|
66
|
+
}}
|
|
67
|
+
ondragend={() => (dragId = overId = null)}
|
|
68
|
+
>
|
|
69
|
+
<label class="sv-fhide-row" title={c.id === primary ? 'The primary field is always shown' : undefined}>
|
|
70
|
+
<input
|
|
71
|
+
type="checkbox"
|
|
72
|
+
role="switch"
|
|
73
|
+
class="sv-fvb-switch"
|
|
74
|
+
checked={c.visible}
|
|
75
|
+
disabled={c.id === primary}
|
|
76
|
+
onchange={(e) => setVisible(c, (e.currentTarget as HTMLInputElement).checked)}
|
|
77
|
+
/>
|
|
78
|
+
<FieldIcon column={c} />
|
|
79
|
+
<span class="sv-fhide-name">{c.header}</span>
|
|
80
|
+
</label>
|
|
81
|
+
{#if c.id !== primary && !query}
|
|
82
|
+
<span class="sv-fhide-grip" aria-hidden="true">
|
|
83
|
+
<svg viewBox="0 0 24 24" width="14" height="14"><path d="M9 6h.01M15 6h.01M9 12h.01M15 12h.01M9 18h.01M15 18h.01" stroke="currentColor" stroke-width="3" stroke-linecap="round" /></svg>
|
|
84
|
+
</span>
|
|
85
|
+
{/if}
|
|
86
|
+
</li>
|
|
87
|
+
{/each}
|
|
88
|
+
</ul>
|
|
89
|
+
<div class="sv-fvb-foot">
|
|
90
|
+
<button type="button" class="sv-fvb-text-btn" onclick={() => setAll(false)}>Hide all</button>
|
|
91
|
+
<button type="button" class="sv-fvb-text-btn" onclick={() => setAll(true)}>Show all</button>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<style>
|
|
96
|
+
.sv-fhide { width: 300px; }
|
|
97
|
+
.sv-fhide-list { list-style: none; margin: 8px 0; padding: 0; max-height: 320px; overflow: auto; }
|
|
98
|
+
.sv-fhide-item { display: flex; align-items: center; gap: 4px; border-radius: 6px; padding: 0 4px; }
|
|
99
|
+
.sv-fhide-item:hover { background: var(--sg-row-hover-bg, #f1f5f9); }
|
|
100
|
+
.sv-fhide-item.is-over { box-shadow: inset 0 2px 0 var(--sg-accent, #2563eb); }
|
|
101
|
+
.sv-fhide-row { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; padding: 5px 0; cursor: pointer; }
|
|
102
|
+
.sv-fhide-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
103
|
+
.sv-fhide-grip { color: var(--sg-muted, #64748b); cursor: grab; display: inline-flex; }
|
|
104
|
+
</style>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* The view bar's "Sort" (and, with `group`, its "Group"): a list of fields
|
|
4
|
+
* each with a direction labeled for its type ("1 → 9", "A → Z"),
|
|
5
|
+
* added from a field picker, removed with the bin.
|
|
6
|
+
*/
|
|
7
|
+
import { panelFocus } from './panel-focus'
|
|
8
|
+
import { fieldColumns, type FieldColumn } from '../grid-access'
|
|
9
|
+
import { sortLabels } from '../view-model'
|
|
10
|
+
import { groupRules, userSorts, writeSortState, type SortRule } from './sort-state'
|
|
11
|
+
import FieldIcon from './FieldIcon.svelte'
|
|
12
|
+
|
|
13
|
+
let { ctrl, api, group = false }: { ctrl: any; api: any; group?: boolean } = $props()
|
|
14
|
+
|
|
15
|
+
// Groups go three deep, as in a spreadsheet outline.
|
|
16
|
+
const MAX_GROUPS = 3
|
|
17
|
+
const NOT_SORTABLE = new Set(['attachment', 'button'])
|
|
18
|
+
const columns = $derived(fieldColumns(ctrl).filter((c) => c.def.sortable !== false && !NOT_SORTABLE.has(c.fieldType ?? '')))
|
|
19
|
+
const rules = $derived(group ? groupRules(ctrl) : userSorts(ctrl))
|
|
20
|
+
const free = $derived(columns.filter((c) => !rules.some((r) => r.id === c.id)))
|
|
21
|
+
const col = (id: string): FieldColumn | undefined => columns.find((c) => c.id === id)
|
|
22
|
+
|
|
23
|
+
function write(next: SortRule[]) {
|
|
24
|
+
if (group) writeSortState(api, next, userSorts(ctrl))
|
|
25
|
+
else writeSortState(api, groupRules(ctrl), next)
|
|
26
|
+
}
|
|
27
|
+
const replace = (i: number, r: SortRule) => write(rules.map((x, j) => (j === i ? r : x)))
|
|
28
|
+
const remove = (i: number) => write(rules.filter((_, j) => j !== i))
|
|
29
|
+
// "+ Add another sort" opens the list of the fields not sorted by yet.
|
|
30
|
+
let adding = $state(false)
|
|
31
|
+
function add(id: string) {
|
|
32
|
+
adding = false
|
|
33
|
+
write([...rules, { id, desc: false }])
|
|
34
|
+
}
|
|
35
|
+
const canAdd = $derived(free.length > 0 && (!group || rules.length < MAX_GROUPS))
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<div class="sv-fvb-panel sv-fsort" use:panelFocus>
|
|
39
|
+
{#if rules.length}
|
|
40
|
+
<p class="sv-fvb-title">{group ? 'Group by' : 'Sort by'}</p>
|
|
41
|
+
{#each rules as r, i (r.id)}
|
|
42
|
+
{@const c = col(r.id)}
|
|
43
|
+
{@const [up, down] = sortLabels(c?.def)}
|
|
44
|
+
<div class="sv-fsort-row">
|
|
45
|
+
<select class="sv-fvb-select" aria-label="Field" value={r.id} onchange={(e) => replace(i, { id: (e.currentTarget as HTMLSelectElement).value, desc: r.desc })}>
|
|
46
|
+
{#each columns.filter((x) => x.id === r.id || !rules.some((y) => y.id === x.id)) as x (x.id)}<option value={x.id}>{x.header}</option>{/each}
|
|
47
|
+
</select>
|
|
48
|
+
<select class="sv-fvb-select" aria-label="Direction" value={r.desc ? 'desc' : 'asc'} onchange={(e) => replace(i, { id: r.id, desc: (e.currentTarget as HTMLSelectElement).value === 'desc' })}>
|
|
49
|
+
<option value="asc">{up}</option>
|
|
50
|
+
<option value="desc">{down}</option>
|
|
51
|
+
</select>
|
|
52
|
+
<button type="button" class="sv-fvb-icon-btn" aria-label={group ? 'Remove group' : 'Remove sort'} onclick={() => remove(i)}>
|
|
53
|
+
<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" /></svg>
|
|
54
|
+
</button>
|
|
55
|
+
</div>
|
|
56
|
+
{/each}
|
|
57
|
+
{/if}
|
|
58
|
+
{#if !rules.length || adding}
|
|
59
|
+
{#if !rules.length}<p class="sv-fvb-title">{group ? 'Group by' : 'Sort by'}</p>{/if}
|
|
60
|
+
<ul class="sv-fsort-fields" role="list" aria-label="Fields">
|
|
61
|
+
{#each free as x (x.id)}
|
|
62
|
+
<li>
|
|
63
|
+
<button type="button" class="sv-fsort-field" onclick={() => add(x.id)}>
|
|
64
|
+
<FieldIcon column={x} />
|
|
65
|
+
<span>{x.header}</span>
|
|
66
|
+
</button>
|
|
67
|
+
</li>
|
|
68
|
+
{/each}
|
|
69
|
+
</ul>
|
|
70
|
+
{:else if canAdd}
|
|
71
|
+
<button type="button" class="sv-fvb-text-btn sv-fsort-add" onclick={() => (adding = true)}>{group ? '+ Add subgroup' : '+ Add another sort'}</button>
|
|
72
|
+
{/if}
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<style>
|
|
76
|
+
.sv-fsort { width: 340px; }
|
|
77
|
+
.sv-fsort-row { display: grid; grid-template-columns: minmax(0, 1fr) 110px 28px; gap: 6px; margin: 6px 0; }
|
|
78
|
+
.sv-fsort-add { margin-top: 8px; }
|
|
79
|
+
.sv-fsort-fields { list-style: none; margin: 0; padding: 0; max-height: 300px; overflow: auto; }
|
|
80
|
+
.sv-fsort-field {
|
|
81
|
+
display: flex; align-items: center; gap: 8px; width: 100%; padding: 5px 6px; border: 0; border-radius: 6px;
|
|
82
|
+
background: none; color: inherit; font: inherit; text-align: start; cursor: pointer;
|
|
83
|
+
}
|
|
84
|
+
.sv-fsort-field:hover { background: var(--sg-row-hover-bg, #f1f5f9); }
|
|
85
|
+
</style>
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* The view bar's view switcher: the saved views (switch, rename, duplicate,
|
|
4
|
+
* delete) and "Create" for a new one of any type the page can show. A
|
|
5
|
+
* Kanban asks what to stack by, a calendar or Gantt which date field, a
|
|
6
|
+
* gallery which attachment field makes the cover.
|
|
7
|
+
*/
|
|
8
|
+
import { panelFocus } from './panel-focus'
|
|
9
|
+
import { coverColumns, dateColumns, dependencyColumns, progressColumns, stackableColumns, viewTypeInfo, type FieldView, type FieldViewType } from '../view-model'
|
|
10
|
+
import { CHART_KINDS, chartXColumns, chartYColumns, defaultChart, type ChartSetting } from '../chart-view'
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
ctrl,
|
|
14
|
+
views,
|
|
15
|
+
activeId,
|
|
16
|
+
types,
|
|
17
|
+
onSwitch,
|
|
18
|
+
onCreate,
|
|
19
|
+
onRename,
|
|
20
|
+
onDuplicate,
|
|
21
|
+
onDelete,
|
|
22
|
+
}: {
|
|
23
|
+
ctrl: any
|
|
24
|
+
views: ReadonlyArray<FieldView>
|
|
25
|
+
activeId: string
|
|
26
|
+
types: ReadonlyArray<FieldViewType>
|
|
27
|
+
onSwitch: (id: string) => void
|
|
28
|
+
onCreate: (view: Omit<FieldView, 'id'>) => void
|
|
29
|
+
onRename: (id: string, name: string) => void
|
|
30
|
+
onDuplicate: (id: string) => void
|
|
31
|
+
onDelete: (id: string) => void
|
|
32
|
+
} = $props()
|
|
33
|
+
|
|
34
|
+
let renaming = $state<string | null>(null)
|
|
35
|
+
let draft = $state<{ type: FieldViewType; name: string; stackBy?: string; dateField?: string; endField?: string; progressField?: string; dependencyField?: string; coverField?: string; chart?: ChartSetting } | null>(null)
|
|
36
|
+
|
|
37
|
+
const stacks = $derived(stackableColumns(ctrl))
|
|
38
|
+
const dates = $derived(dateColumns(ctrl))
|
|
39
|
+
const covers = $derived(coverColumns(ctrl))
|
|
40
|
+
const progresses = $derived(progressColumns(ctrl))
|
|
41
|
+
const dependencies = $derived(dependencyColumns(ctrl))
|
|
42
|
+
const chartXs = $derived(chartXColumns(ctrl))
|
|
43
|
+
const chartYs = $derived(chartYColumns(ctrl))
|
|
44
|
+
/** A type the table can show: a Kanban needs a field to stack by, a calendar a date, a chart a field to group by. */
|
|
45
|
+
const possible = (t: FieldViewType) =>
|
|
46
|
+
t === 'kanban' ? stacks.length > 0 : t === 'calendar' || t === 'gantt' ? dates.length > 0 : t === 'chart' ? chartXs.length > 0 : true
|
|
47
|
+
|
|
48
|
+
function nameFor(t: FieldViewType): string {
|
|
49
|
+
const base = viewTypeInfo(t).label
|
|
50
|
+
let n = 1
|
|
51
|
+
const taken = new Set(views.map((v) => v.name))
|
|
52
|
+
while (taken.has(n === 1 ? base : `${base} ${n}`)) n += 1
|
|
53
|
+
return n === 1 ? base : `${base} ${n}`
|
|
54
|
+
}
|
|
55
|
+
function start(t: FieldViewType) {
|
|
56
|
+
draft = {
|
|
57
|
+
type: t,
|
|
58
|
+
name: nameFor(t),
|
|
59
|
+
...(t === 'kanban' ? { stackBy: stacks[0]?.id } : {}),
|
|
60
|
+
...(t === 'calendar' || t === 'gantt' ? { dateField: dates[0]?.id, endField: t === 'gantt' ? dates[1]?.id : undefined } : {}),
|
|
61
|
+
// A Gantt starts with the table's progress and depends-on fields, when it has them.
|
|
62
|
+
...(t === 'gantt' ? { progressField: progresses[0]?.id, dependencyField: dependencies[0]?.id } : {}),
|
|
63
|
+
...(t === 'gallery' && covers[0] ? { coverField: covers[0].id } : {}),
|
|
64
|
+
...(t === 'chart' ? { chart: defaultChart(ctrl) ?? undefined } : {}),
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function create() {
|
|
68
|
+
if (!draft) return
|
|
69
|
+
const d = draft
|
|
70
|
+
draft = null
|
|
71
|
+
onCreate({ ...d, name: d.name.trim() || nameFor(d.type) })
|
|
72
|
+
}
|
|
73
|
+
function focusSelect(node: HTMLInputElement) {
|
|
74
|
+
node.focus()
|
|
75
|
+
node.select()
|
|
76
|
+
}
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<div class="sv-fvb-panel sv-fviews" use:panelFocus>
|
|
80
|
+
{#if draft}
|
|
81
|
+
{@const d = draft}
|
|
82
|
+
<p class="sv-fvb-title">New {viewTypeInfo(d.type).label.toLowerCase()} view</p>
|
|
83
|
+
<label class="sv-fviews-field">
|
|
84
|
+
<span>Name</span>
|
|
85
|
+
<input class="sv-fvb-input" bind:value={d.name} use:focusSelect onkeydown={(e) => {
|
|
86
|
+
if (e.key === 'Enter') create()
|
|
87
|
+
}} />
|
|
88
|
+
</label>
|
|
89
|
+
{#if d.type === 'kanban'}
|
|
90
|
+
<label class="sv-fviews-field">
|
|
91
|
+
<span>Stack records by</span>
|
|
92
|
+
<select class="sv-fvb-select" bind:value={d.stackBy}>
|
|
93
|
+
{#each stacks as c (c.id)}<option value={c.id}>{c.header}</option>{/each}
|
|
94
|
+
</select>
|
|
95
|
+
</label>
|
|
96
|
+
{:else if d.type === 'calendar' || d.type === 'gantt'}
|
|
97
|
+
<label class="sv-fviews-field">
|
|
98
|
+
<span>{d.type === 'gantt' ? 'Start date' : 'Date field'}</span>
|
|
99
|
+
<select class="sv-fvb-select" bind:value={d.dateField}>
|
|
100
|
+
{#each dates as c (c.id)}<option value={c.id}>{c.header}</option>{/each}
|
|
101
|
+
</select>
|
|
102
|
+
</label>
|
|
103
|
+
<label class="sv-fviews-field">
|
|
104
|
+
<span>End date</span>
|
|
105
|
+
<select class="sv-fvb-select" bind:value={d.endField}>
|
|
106
|
+
<option value={undefined}>None</option>
|
|
107
|
+
{#each dates.filter((c) => c.id !== d.dateField) as c (c.id)}<option value={c.id}>{c.header}</option>{/each}
|
|
108
|
+
</select>
|
|
109
|
+
</label>
|
|
110
|
+
{#if d.type === 'gantt' && progresses.length}
|
|
111
|
+
<label class="sv-fviews-field">
|
|
112
|
+
<span>Progress</span>
|
|
113
|
+
<select class="sv-fvb-select" bind:value={d.progressField}>
|
|
114
|
+
<option value={undefined}>None</option>
|
|
115
|
+
{#each progresses as c (c.id)}<option value={c.id}>{c.header}</option>{/each}
|
|
116
|
+
</select>
|
|
117
|
+
</label>
|
|
118
|
+
{/if}
|
|
119
|
+
{#if d.type === 'gantt' && dependencies.length}
|
|
120
|
+
<label class="sv-fviews-field">
|
|
121
|
+
<span>Depends on</span>
|
|
122
|
+
<select class="sv-fvb-select" bind:value={d.dependencyField}>
|
|
123
|
+
<option value={undefined}>None</option>
|
|
124
|
+
{#each dependencies as c (c.id)}<option value={c.id}>{c.header}</option>{/each}
|
|
125
|
+
</select>
|
|
126
|
+
</label>
|
|
127
|
+
{/if}
|
|
128
|
+
{:else if d.type === 'chart' && d.chart}
|
|
129
|
+
{@const c = d.chart}
|
|
130
|
+
<label class="sv-fviews-field">
|
|
131
|
+
<span>Chart type</span>
|
|
132
|
+
<select class="sv-fvb-select" bind:value={c.kind}>
|
|
133
|
+
{#each CHART_KINDS as k (k.kind)}<option value={k.kind}>{k.label}</option>{/each}
|
|
134
|
+
</select>
|
|
135
|
+
</label>
|
|
136
|
+
<label class="sv-fviews-field">
|
|
137
|
+
<span>Group by</span>
|
|
138
|
+
<select class="sv-fvb-select" bind:value={c.x}>
|
|
139
|
+
{#each chartXs as col (col.id)}<option value={col.id}>{col.header}</option>{/each}
|
|
140
|
+
</select>
|
|
141
|
+
</label>
|
|
142
|
+
<label class="sv-fviews-field">
|
|
143
|
+
<span>Value</span>
|
|
144
|
+
<select class="sv-fvb-select" bind:value={c.y}>
|
|
145
|
+
<option value={undefined}>Number of records</option>
|
|
146
|
+
{#each chartYs as col (col.id)}<option value={col.id}>Sum of {col.header}</option>{/each}
|
|
147
|
+
</select>
|
|
148
|
+
</label>
|
|
149
|
+
{:else if d.type === 'gallery'}
|
|
150
|
+
<label class="sv-fviews-field">
|
|
151
|
+
<span>Cover image</span>
|
|
152
|
+
<select class="sv-fvb-select" bind:value={d.coverField}>
|
|
153
|
+
<option value={undefined}>None</option>
|
|
154
|
+
{#each covers as c (c.id)}<option value={c.id}>{c.header}</option>{/each}
|
|
155
|
+
</select>
|
|
156
|
+
</label>
|
|
157
|
+
{/if}
|
|
158
|
+
<div class="sv-fvb-foot sv-fviews-actions">
|
|
159
|
+
<button type="button" class="sv-fvb-text-btn" onclick={() => (draft = null)}>Cancel</button>
|
|
160
|
+
<button type="button" class="sv-fvb-solid-btn" onclick={create}>Create view</button>
|
|
161
|
+
</div>
|
|
162
|
+
{:else}
|
|
163
|
+
<ul class="sv-fviews-list" role="list">
|
|
164
|
+
{#each views as v (v.id)}
|
|
165
|
+
<li class="sv-fviews-item" class:is-active={v.id === activeId}>
|
|
166
|
+
{#if renaming === v.id}
|
|
167
|
+
<input
|
|
168
|
+
class="sv-fvb-input"
|
|
169
|
+
aria-label="View name"
|
|
170
|
+
value={v.name}
|
|
171
|
+
use:focusSelect
|
|
172
|
+
onkeydown={(e) => {
|
|
173
|
+
if (e.key === 'Enter') {
|
|
174
|
+
onRename(v.id, (e.currentTarget as HTMLInputElement).value.trim() || v.name)
|
|
175
|
+
renaming = null
|
|
176
|
+
} else if (e.key === 'Escape') {
|
|
177
|
+
e.stopPropagation()
|
|
178
|
+
renaming = null
|
|
179
|
+
}
|
|
180
|
+
}}
|
|
181
|
+
onblur={(e) => {
|
|
182
|
+
onRename(v.id, (e.currentTarget as HTMLInputElement).value.trim() || v.name)
|
|
183
|
+
renaming = null
|
|
184
|
+
}}
|
|
185
|
+
/>
|
|
186
|
+
{:else}
|
|
187
|
+
<button type="button" class="sv-fviews-pick" aria-current={v.id === activeId ? 'true' : undefined} onclick={() => onSwitch(v.id)}>
|
|
188
|
+
<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path d={viewTypeInfo(v.type).icon} fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
|
189
|
+
<span>{v.name}</span>
|
|
190
|
+
</button>
|
|
191
|
+
<span class="sv-fviews-acts">
|
|
192
|
+
<button type="button" class="sv-fvb-icon-btn" aria-label={`Rename ${v.name}`} title="Rename" onclick={() => (renaming = v.id)}>
|
|
193
|
+
<svg viewBox="0 0 24 24" width="13" height="13" aria-hidden="true"><path d="M4 20h4L19 9l-4-4L4 16z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round" /></svg>
|
|
194
|
+
</button>
|
|
195
|
+
<button type="button" class="sv-fvb-icon-btn" aria-label={`Duplicate ${v.name}`} title="Duplicate" onclick={() => onDuplicate(v.id)}>
|
|
196
|
+
<svg viewBox="0 0 24 24" width="13" height="13" aria-hidden="true"><path d="M8 8h12v12H8zM16 8V4H4v12h4" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round" /></svg>
|
|
197
|
+
</button>
|
|
198
|
+
{#if views.length > 1}
|
|
199
|
+
<button type="button" class="sv-fvb-icon-btn" aria-label={`Delete ${v.name}`} title="Delete" onclick={() => onDelete(v.id)}>
|
|
200
|
+
<svg viewBox="0 0 24 24" width="13" height="13" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round" /></svg>
|
|
201
|
+
</button>
|
|
202
|
+
{/if}
|
|
203
|
+
</span>
|
|
204
|
+
{/if}
|
|
205
|
+
</li>
|
|
206
|
+
{/each}
|
|
207
|
+
</ul>
|
|
208
|
+
<p class="sv-fvb-title sv-fviews-create">Create</p>
|
|
209
|
+
<ul class="sv-fviews-types" role="list">
|
|
210
|
+
{#each types as t (t)}
|
|
211
|
+
{@const info = viewTypeInfo(t)}
|
|
212
|
+
<li>
|
|
213
|
+
<button
|
|
214
|
+
type="button"
|
|
215
|
+
class="sv-fviews-type"
|
|
216
|
+
disabled={!possible(t)}
|
|
217
|
+
title={possible(t) ? undefined : t === 'kanban' ? 'Needs a single select field' : t === 'chart' ? 'Needs a field to group by' : 'Needs a date field'}
|
|
218
|
+
onclick={() => start(t)}
|
|
219
|
+
>
|
|
220
|
+
<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path d={info.icon} fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
|
221
|
+
<span>{info.label}</span>
|
|
222
|
+
<svg class="sv-fviews-plus" viewBox="0 0 24 24" width="13" height="13" aria-hidden="true"><path d="M12 5v14M5 12h14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" /></svg>
|
|
223
|
+
</button>
|
|
224
|
+
</li>
|
|
225
|
+
{/each}
|
|
226
|
+
</ul>
|
|
227
|
+
{/if}
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<style>
|
|
231
|
+
.sv-fviews { width: 260px; }
|
|
232
|
+
.sv-fviews-list, .sv-fviews-types { list-style: none; margin: 0; padding: 0; }
|
|
233
|
+
.sv-fviews-item { display: flex; align-items: center; gap: 2px; border-radius: 6px; }
|
|
234
|
+
.sv-fviews-item:hover, .sv-fviews-item.is-active { background: var(--sg-row-hover-bg, #f1f5f9); }
|
|
235
|
+
.sv-fviews-item .sv-fviews-acts { display: none; }
|
|
236
|
+
.sv-fviews-item:hover .sv-fviews-acts, .sv-fviews-item:focus-within .sv-fviews-acts { display: inline-flex; }
|
|
237
|
+
.sv-fviews-pick, .sv-fviews-type {
|
|
238
|
+
display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; padding: 6px 8px; border: 0; border-radius: 6px;
|
|
239
|
+
background: none; color: inherit; font: inherit; text-align: start; cursor: pointer;
|
|
240
|
+
}
|
|
241
|
+
.sv-fviews-pick span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
242
|
+
.sv-fviews-item.is-active .sv-fviews-pick { font-weight: 600; }
|
|
243
|
+
.sv-fviews-type:hover:not(:disabled) { background: var(--sg-row-hover-bg, #f1f5f9); }
|
|
244
|
+
.sv-fviews-type:disabled { opacity: 0.45; cursor: default; }
|
|
245
|
+
.sv-fviews-plus { margin-inline-start: auto; color: var(--sg-muted, #64748b); }
|
|
246
|
+
.sv-fviews-create { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--sg-border, #e2e8f0); }
|
|
247
|
+
.sv-fviews-field { display: grid; gap: 4px; margin: 8px 0; }
|
|
248
|
+
.sv-fviews-field > span { font-size: 12px; color: var(--sg-muted, #64748b); }
|
|
249
|
+
.sv-fviews-actions { justify-content: flex-end; }
|
|
250
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type FieldView, type FieldViewType } from '../view-model.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
ctrl: any;
|
|
4
|
+
views: ReadonlyArray<FieldView>;
|
|
5
|
+
activeId: string;
|
|
6
|
+
types: ReadonlyArray<FieldViewType>;
|
|
7
|
+
onSwitch: (id: string) => void;
|
|
8
|
+
onCreate: (view: Omit<FieldView, 'id'>) => void;
|
|
9
|
+
onRename: (id: string, name: string) => void;
|
|
10
|
+
onDuplicate: (id: string) => void;
|
|
11
|
+
onDelete: (id: string) => void;
|
|
12
|
+
};
|
|
13
|
+
declare const ViewsMenu: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
14
|
+
type ViewsMenu = ReturnType<typeof ViewsMenu>;
|
|
15
|
+
export default ViewsMenu;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The view bar's panels open in a layer on <body>, after the bar in the page,
|
|
3
|
+
* so Tab from the button would not reach them. On open a panel takes focus
|
|
4
|
+
* (its first control); on close, if focus was still in it, focus goes back to
|
|
5
|
+
* what had it before (the button that opened it).
|
|
6
|
+
*/
|
|
7
|
+
const FOCUSABLE = 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
8
|
+
export function panelFocus(node) {
|
|
9
|
+
const before = document.activeElement;
|
|
10
|
+
queueMicrotask(() => {
|
|
11
|
+
if (!node.isConnected || node.contains(document.activeElement))
|
|
12
|
+
return;
|
|
13
|
+
node.querySelector(FOCUSABLE)?.focus();
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
destroy() {
|
|
17
|
+
const active = document.activeElement;
|
|
18
|
+
if ((!active || active === document.body || node.contains(active)) && before?.isConnected)
|
|
19
|
+
before.focus();
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sorts and groups share the grid's sort state: a grouped field's direction
|
|
3
|
+
* is a sort entry ahead of the sorts the user picked, so the groups come in
|
|
4
|
+
* that order. These read the two apart and write them back together.
|
|
5
|
+
*/
|
|
6
|
+
export type SortRule = {
|
|
7
|
+
id: string;
|
|
8
|
+
desc: boolean;
|
|
9
|
+
};
|
|
10
|
+
/** The grid's sorting and grouping, read so a `$derived` follows them. */
|
|
11
|
+
export declare function sortState(ctrl: any): {
|
|
12
|
+
sorting: SortRule[];
|
|
13
|
+
grouping: string[];
|
|
14
|
+
};
|
|
15
|
+
/** The user's sorts: every sort entry that is not a group's direction. */
|
|
16
|
+
export declare const userSorts: (ctrl: any) => SortRule[];
|
|
17
|
+
/** Each grouped field with its direction (ascending when it has no entry). */
|
|
18
|
+
export declare const groupRules: (ctrl: any) => SortRule[];
|
|
19
|
+
/** Write groups and sorts back as one sort state (groups first) and the grouping. */
|
|
20
|
+
export declare function writeSortState(api: any, groups: SortRule[], sorts: SortRule[]): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The grid's sorting and grouping, read so a `$derived` follows them. */
|
|
2
|
+
export function sortState(ctrl) {
|
|
3
|
+
void ctrl.gridStateVersion;
|
|
4
|
+
const s = ctrl.grid?.getState?.() ?? {};
|
|
5
|
+
return { sorting: [...(s.sorting ?? [])], grouping: [...(s.grouping ?? [])] };
|
|
6
|
+
}
|
|
7
|
+
/** The user's sorts: every sort entry that is not a group's direction. */
|
|
8
|
+
export const userSorts = (ctrl) => {
|
|
9
|
+
const { sorting, grouping } = sortState(ctrl);
|
|
10
|
+
return sorting.filter((s) => !grouping.includes(s.id));
|
|
11
|
+
};
|
|
12
|
+
/** Each grouped field with its direction (ascending when it has no entry). */
|
|
13
|
+
export const groupRules = (ctrl) => {
|
|
14
|
+
const { sorting, grouping } = sortState(ctrl);
|
|
15
|
+
return grouping.map((id) => ({ id, desc: !!sorting.find((s) => s.id === id)?.desc }));
|
|
16
|
+
};
|
|
17
|
+
/** Write groups and sorts back as one sort state (groups first) and the grouping. */
|
|
18
|
+
export function writeSortState(api, groups, sorts) {
|
|
19
|
+
const grouped = groups.map((g) => g.id);
|
|
20
|
+
api.setState({
|
|
21
|
+
sorting: [...groups, ...sorts.filter((s) => !grouped.includes(s.id))],
|
|
22
|
+
grouping: grouped,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register the Enterprise gallery view. Idempotent - safe to call from every
|
|
3
|
+
* component that uses `gallery`, or once at app start. Also invoked by
|
|
4
|
+
* {@link installEnterprise} so wiring the Pro API turns the view on too.
|
|
5
|
+
*/
|
|
6
|
+
export declare function enableGalleryView(): void;
|
|
7
|
+
export { default as SvGridGallery } from './SvGridGallery.svelte';
|
package/dist/gallery.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gallery (view) - registers the Enterprise gallery renderer with the grid so
|
|
3
|
+
* `<SvGrid gallery={...}>` renders its rows as cards instead of the table.
|
|
4
|
+
* The grid ships the `gallery` prop and its config type for free; the
|
|
5
|
+
* *renderer* (SvGridGallery) is Pro and plugs in through the grid's
|
|
6
|
+
* `registerGalleryView` seam.
|
|
7
|
+
*
|
|
8
|
+
* Like the rest of Enterprise it runs without a license key during the
|
|
9
|
+
* evaluation; the grid shows the watermark and a one-time console notice.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { setLicenseKey, enableGalleryView } from '@svgrid/enterprise'
|
|
13
|
+
* setLicenseKey('YOUR-KEY')
|
|
14
|
+
* enableGalleryView()
|
|
15
|
+
* // then: <SvGrid {data} {columns} gallery={{ coverField: 'photo' }} />
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { registerGalleryView } from '@svgrid/grid';
|
|
19
|
+
import { hasValidLicense } from './license.js';
|
|
20
|
+
import { emitUnlicensedNudge } from './watermark.js';
|
|
21
|
+
import SvGridGallery from './SvGridGallery.svelte';
|
|
22
|
+
let enabled = false;
|
|
23
|
+
/**
|
|
24
|
+
* Register the Enterprise gallery view. Idempotent - safe to call from every
|
|
25
|
+
* component that uses `gallery`, or once at app start. Also invoked by
|
|
26
|
+
* {@link installEnterprise} so wiring the Pro API turns the view on too.
|
|
27
|
+
*/
|
|
28
|
+
export function enableGalleryView() {
|
|
29
|
+
if (enabled)
|
|
30
|
+
return;
|
|
31
|
+
enabled = true;
|
|
32
|
+
registerGalleryView(SvGridGallery);
|
|
33
|
+
if (!hasValidLicense())
|
|
34
|
+
emitUnlicensedNudge();
|
|
35
|
+
}
|
|
36
|
+
export { default as SvGridGallery } from './SvGridGallery.svelte';
|