@svgrid/enterprise 3.1.8 → 3.1.10

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 (268) hide show
  1. package/README.md +1 -0
  2. package/dist/SvAlertRuleEditor.svelte +16 -5
  3. package/dist/SvGridAlerts.svelte +8 -0
  4. package/dist/SvGridAlerts.svelte.d.ts +4 -0
  5. package/dist/SvGridBoard.svelte +69 -10
  6. package/dist/SvGridBulkEditDrawer.svelte +13 -5
  7. package/dist/SvGridGallery.svelte +286 -0
  8. package/dist/SvGridGallery.svelte.d.ts +30 -0
  9. package/dist/SvGridScheduler.svelte +33 -11
  10. package/dist/SvGridSelectionBar.svelte +47 -0
  11. package/dist/advanced-filter/expr-columns-from-grid.d.ts +1 -0
  12. package/dist/advanced-filter/expr-columns-from-grid.js +11 -7
  13. package/dist/advanced-filter-enable.d.ts +6 -0
  14. package/dist/advanced-filter-enable.js +8 -0
  15. package/dist/alerts/alert-engine.d.ts +7 -0
  16. package/dist/alerts/alert-engine.js +5 -2
  17. package/dist/alerts/condition-editor.d.ts +26 -0
  18. package/dist/alerts/condition-editor.js +17 -0
  19. package/dist/base/index.d.ts +20 -0
  20. package/dist/base/index.js +20 -0
  21. package/dist/bulk-edit.d.ts +11 -0
  22. package/dist/bulk-edit.js +18 -1
  23. package/dist/cdn/svgrid-enterprise.svelte-external.js +11197 -10639
  24. package/dist/designer/assets/GridFooter-Cu8_yBlQ.js +2 -0
  25. package/dist/designer/assets/GridMenus-BUjkkAU2.js +6 -0
  26. package/dist/designer/assets/PinSplitter-BgTpgh1d.css +1 -0
  27. package/dist/designer/assets/PinSplitter-C5AwA1xV.js +1 -0
  28. package/dist/designer/assets/SvGrid.helpers-CIrmIo9U.js +1 -0
  29. package/dist/designer/assets/SvGridCellEditor-CJxThKL_.js +4 -0
  30. package/dist/designer/assets/SvListBox-CS7oUAiX.js +2 -0
  31. package/dist/designer/assets/conditional-formatting-MDWbF8_Y.js +1 -0
  32. package/dist/designer/assets/grid-extension.svelte-BZ7AqyJI.js +1 -0
  33. package/dist/designer/assets/index-BmvG99kR.css +1 -0
  34. package/dist/designer/assets/{index-Obk8sI77.js → index-CJodU3tU.js} +307 -307
  35. package/dist/designer/assets/sparkline-C-NcnB2i.js +1 -0
  36. package/dist/designer/assets/src-C_U1NKOY.js +58 -0
  37. package/dist/designer/assets/sv-grid-scrollbar-B9N6Wuj0.js +109 -0
  38. package/dist/designer/assets/validate-H5u2HEQh.js +1 -0
  39. package/dist/designer/index.html +11 -7
  40. package/dist/expressions/compile.js +10 -0
  41. package/dist/expressions/evaluate.js +18 -0
  42. package/dist/expressions/expression-types.d.ts +10 -0
  43. package/dist/expressions/kinds.d.ts +28 -0
  44. package/dist/expressions/kinds.js +19 -0
  45. package/dist/expressions/parse.js +9 -1
  46. package/dist/fields/AttachmentCell.svelte +242 -0
  47. package/dist/fields/AttachmentCell.svelte.d.ts +5 -0
  48. package/dist/fields/AttachmentDialog.svelte +357 -0
  49. package/dist/fields/AttachmentDialog.svelte.d.ts +13 -0
  50. package/dist/fields/AttachmentEditor.svelte +41 -0
  51. package/dist/fields/AttachmentEditor.svelte.d.ts +11 -0
  52. package/dist/fields/AttachmentPreview.svelte +72 -0
  53. package/dist/fields/AttachmentPreview.svelte.d.ts +8 -0
  54. package/dist/fields/BarcodeCell.svelte +20 -0
  55. package/dist/fields/BarcodeCell.svelte.d.ts +5 -0
  56. package/dist/fields/BaseAutomations.svelte +558 -0
  57. package/dist/fields/BaseAutomations.svelte.d.ts +18 -0
  58. package/dist/fields/BaseChartView.svelte +47 -0
  59. package/dist/fields/BaseChartView.svelte.d.ts +8 -0
  60. package/dist/fields/BaseTableView.svelte +185 -0
  61. package/dist/fields/BaseTableView.svelte.d.ts +37 -0
  62. package/dist/fields/BaseTabs.svelte +308 -0
  63. package/dist/fields/BaseTabs.svelte.d.ts +21 -0
  64. package/dist/fields/ButtonCell.svelte +47 -0
  65. package/dist/fields/ButtonCell.svelte.d.ts +20 -0
  66. package/dist/fields/CheckboxCell.svelte +46 -0
  67. package/dist/fields/CheckboxCell.svelte.d.ts +14 -0
  68. package/dist/fields/DeleteFieldConfirm.svelte +80 -0
  69. package/dist/fields/DeleteFieldConfirm.svelte.d.ts +8 -0
  70. package/dist/fields/DeleteRecordConfirm.svelte +48 -0
  71. package/dist/fields/DeleteRecordConfirm.svelte.d.ts +9 -0
  72. package/dist/fields/FieldCell.svelte +80 -0
  73. package/dist/fields/FieldCell.svelte.d.ts +9 -0
  74. package/dist/fields/FieldEditor.svelte +321 -0
  75. package/dist/fields/FieldEditor.svelte.d.ts +10 -0
  76. package/dist/fields/FieldInput.svelte +188 -0
  77. package/dist/fields/FieldInput.svelte.d.ts +29 -0
  78. package/dist/fields/FieldsHeaderEnd.svelte +146 -0
  79. package/dist/fields/FieldsHeaderEnd.svelte.d.ts +6 -0
  80. package/dist/fields/FootRowAdder.svelte +8 -0
  81. package/dist/fields/FootRowAdder.svelte.d.ts +6 -0
  82. package/dist/fields/GroupHeader.svelte +127 -0
  83. package/dist/fields/GroupHeader.svelte.d.ts +24 -0
  84. package/dist/fields/LinkCell.svelte +46 -0
  85. package/dist/fields/LinkCell.svelte.d.ts +9 -0
  86. package/dist/fields/LinkPickerEditor.svelte +172 -0
  87. package/dist/fields/LinkPickerEditor.svelte.d.ts +9 -0
  88. package/dist/fields/LinkPreview.svelte +60 -0
  89. package/dist/fields/LinkPreview.svelte.d.ts +7 -0
  90. package/dist/fields/LinkedRecordView.svelte +94 -0
  91. package/dist/fields/LinkedRecordView.svelte.d.ts +9 -0
  92. package/dist/fields/LinkedRecordsCell.svelte +59 -0
  93. package/dist/fields/LinkedRecordsCell.svelte.d.ts +11 -0
  94. package/dist/fields/LongTextEditor.svelte +157 -0
  95. package/dist/fields/LongTextEditor.svelte.d.ts +11 -0
  96. package/dist/fields/NumberEditor.svelte +309 -0
  97. package/dist/fields/NumberEditor.svelte.d.ts +12 -0
  98. package/dist/fields/OptionsEditor.svelte +117 -0
  99. package/dist/fields/OptionsEditor.svelte.d.ts +11 -0
  100. package/dist/fields/PercentCell.svelte +21 -0
  101. package/dist/fields/PercentCell.svelte.d.ts +8 -0
  102. package/dist/fields/PropertyInput.svelte +204 -0
  103. package/dist/fields/PropertyInput.svelte.d.ts +11 -0
  104. package/dist/fields/RatingCell.svelte +75 -0
  105. package/dist/fields/RatingCell.svelte.d.ts +16 -0
  106. package/dist/fields/RatingEditor.svelte +82 -0
  107. package/dist/fields/RatingEditor.svelte.d.ts +9 -0
  108. package/dist/fields/RecordView.svelte +310 -0
  109. package/dist/fields/RecordView.svelte.d.ts +11 -0
  110. package/dist/fields/RichTextCell.svelte +30 -0
  111. package/dist/fields/RichTextCell.svelte.d.ts +5 -0
  112. package/dist/fields/RowAdder.svelte +93 -0
  113. package/dist/fields/RowAdder.svelte.d.ts +7 -0
  114. package/dist/fields/RowHeader.svelte +99 -0
  115. package/dist/fields/RowHeader.svelte.d.ts +11 -0
  116. package/dist/fields/SelectCell.svelte +35 -0
  117. package/dist/fields/SelectCell.svelte.d.ts +15 -0
  118. package/dist/fields/SelectEditor.svelte +350 -0
  119. package/dist/fields/SelectEditor.svelte.d.ts +17 -0
  120. package/dist/fields/SummaryCell.svelte +160 -0
  121. package/dist/fields/SummaryCell.svelte.d.ts +11 -0
  122. package/dist/fields/SvBase.svelte +545 -0
  123. package/dist/fields/SvBase.svelte.d.ts +30 -0
  124. package/dist/fields/SvBaseForm.svelte +295 -0
  125. package/dist/fields/SvBaseForm.svelte.d.ts +22 -0
  126. package/dist/fields/SvBaseViewBar.svelte +644 -0
  127. package/dist/fields/SvBaseViewBar.svelte.d.ts +26 -0
  128. package/dist/fields/UserCell.svelte +39 -0
  129. package/dist/fields/UserCell.svelte.d.ts +9 -0
  130. package/dist/fields/UserFace.svelte +30 -0
  131. package/dist/fields/UserFace.svelte.d.ts +12 -0
  132. package/dist/fields/activity.svelte.d.ts +50 -0
  133. package/dist/fields/activity.svelte.js +110 -0
  134. package/dist/fields/attachments.d.ts +95 -0
  135. package/dist/fields/attachments.js +145 -0
  136. package/dist/fields/automation-outbound.d.ts +46 -0
  137. package/dist/fields/automation-outbound.js +61 -0
  138. package/dist/fields/automation-server.d.ts +53 -0
  139. package/dist/fields/automation-server.js +333 -0
  140. package/dist/fields/automations.svelte.d.ts +208 -0
  141. package/dist/fields/automations.svelte.js +437 -0
  142. package/dist/fields/bar-request.svelte.d.ts +16 -0
  143. package/dist/fields/bar-request.svelte.js +15 -0
  144. package/dist/fields/base-alerts.d.ts +24 -0
  145. package/dist/fields/base-alerts.js +64 -0
  146. package/dist/fields/base-connect.d.ts +82 -0
  147. package/dist/fields/base-connect.js +271 -0
  148. package/dist/fields/base-schema.d.ts +104 -0
  149. package/dist/fields/base-schema.js +266 -0
  150. package/dist/fields/base-sql.d.ts +62 -0
  151. package/dist/fields/base-sql.js +251 -0
  152. package/dist/fields/base-sync.d.ts +142 -0
  153. package/dist/fields/base-sync.js +499 -0
  154. package/dist/fields/base-tables.d.ts +89 -0
  155. package/dist/fields/base-tables.js +349 -0
  156. package/dist/fields/builtin-types.d.ts +13 -0
  157. package/dist/fields/builtin-types.js +773 -0
  158. package/dist/fields/button.d.ts +2 -0
  159. package/dist/fields/button.js +17 -0
  160. package/dist/fields/chart-view.d.ts +35 -0
  161. package/dist/fields/chart-view.js +154 -0
  162. package/dist/fields/column-order.d.ts +6 -0
  163. package/dist/fields/column-order.js +12 -0
  164. package/dist/fields/convert.d.ts +16 -0
  165. package/dist/fields/convert.js +176 -0
  166. package/dist/fields/copy-text.d.ts +13 -0
  167. package/dist/fields/copy-text.js +45 -0
  168. package/dist/fields/editor-request.svelte.d.ts +71 -0
  169. package/dist/fields/editor-request.svelte.js +52 -0
  170. package/dist/fields/field-ops.d.ts +72 -0
  171. package/dist/fields/field-ops.js +334 -0
  172. package/dist/fields/filter-model.d.ts +84 -0
  173. package/dist/fields/filter-model.js +446 -0
  174. package/dist/fields/format.d.ts +109 -0
  175. package/dist/fields/format.js +288 -0
  176. package/dist/fields/formula.d.ts +50 -0
  177. package/dist/fields/formula.js +530 -0
  178. package/dist/fields/grid-access.d.ts +60 -0
  179. package/dist/fields/grid-access.js +120 -0
  180. package/dist/fields/index.d.ts +34 -0
  181. package/dist/fields/index.js +312 -0
  182. package/dist/fields/linked-fields.d.ts +8 -0
  183. package/dist/fields/linked-fields.js +40 -0
  184. package/dist/fields/links.d.ts +10 -0
  185. package/dist/fields/links.js +46 -0
  186. package/dist/fields/name-list.d.ts +16 -0
  187. package/dist/fields/name-list.js +49 -0
  188. package/dist/fields/palette.d.ts +47 -0
  189. package/dist/fields/palette.js +69 -0
  190. package/dist/fields/record-delete.svelte.d.ts +12 -0
  191. package/dist/fields/record-delete.svelte.js +5 -0
  192. package/dist/fields/record-menu.d.ts +9 -0
  193. package/dist/fields/record-menu.js +78 -0
  194. package/dist/fields/registry.svelte.d.ts +176 -0
  195. package/dist/fields/registry.svelte.js +51 -0
  196. package/dist/fields/rich-text.d.ts +23 -0
  197. package/dist/fields/rich-text.js +48 -0
  198. package/dist/fields/row-color-choice.svelte.d.ts +31 -0
  199. package/dist/fields/row-color-choice.svelte.js +25 -0
  200. package/dist/fields/row-colors.d.ts +36 -0
  201. package/dist/fields/row-colors.js +90 -0
  202. package/dist/fields/row-height.d.ts +28 -0
  203. package/dist/fields/row-height.js +50 -0
  204. package/dist/fields/sources.svelte.d.ts +80 -0
  205. package/dist/fields/sources.svelte.js +131 -0
  206. package/dist/fields/summaries.d.ts +18 -0
  207. package/dist/fields/summaries.js +138 -0
  208. package/dist/fields/view-model.d.ts +129 -0
  209. package/dist/fields/view-model.js +492 -0
  210. package/dist/fields/viewbar/ChartPanel.svelte +61 -0
  211. package/dist/fields/viewbar/ChartPanel.svelte.d.ts +9 -0
  212. package/dist/fields/viewbar/ColorPanel.svelte +112 -0
  213. package/dist/fields/viewbar/ColorPanel.svelte.d.ts +9 -0
  214. package/dist/fields/viewbar/ConditionValue.svelte +153 -0
  215. package/dist/fields/viewbar/ConditionValue.svelte.d.ts +15 -0
  216. package/dist/fields/viewbar/FieldIcon.svelte +18 -0
  217. package/dist/fields/viewbar/FieldIcon.svelte.d.ts +9 -0
  218. package/dist/fields/viewbar/FilterPanel.svelte +192 -0
  219. package/dist/fields/viewbar/FilterPanel.svelte.d.ts +13 -0
  220. package/dist/fields/viewbar/GanttPanel.svelte +60 -0
  221. package/dist/fields/viewbar/GanttPanel.svelte.d.ts +10 -0
  222. package/dist/fields/viewbar/HideFieldsPanel.svelte +104 -0
  223. package/dist/fields/viewbar/HideFieldsPanel.svelte.d.ts +7 -0
  224. package/dist/fields/viewbar/SortPanel.svelte +85 -0
  225. package/dist/fields/viewbar/SortPanel.svelte.d.ts +8 -0
  226. package/dist/fields/viewbar/ViewsMenu.svelte +250 -0
  227. package/dist/fields/viewbar/ViewsMenu.svelte.d.ts +15 -0
  228. package/dist/fields/viewbar/panel-focus.d.ts +3 -0
  229. package/dist/fields/viewbar/panel-focus.js +22 -0
  230. package/dist/fields/viewbar/sort-state.d.ts +20 -0
  231. package/dist/fields/viewbar/sort-state.js +24 -0
  232. package/dist/gallery.d.ts +7 -0
  233. package/dist/gallery.js +36 -0
  234. package/dist/gantt/SvGridGantt.svelte +88 -16
  235. package/dist/gantt/gantt-config.d.ts +28 -0
  236. package/dist/index.d.ts +3 -0
  237. package/dist/index.js +6 -0
  238. package/dist/install.js +3 -0
  239. package/dist/license-core.d.ts +3 -3
  240. package/dist/license.js +1 -0
  241. package/dist/node/studio.js +105 -4
  242. package/dist/scheduler-config.d.ts +11 -0
  243. package/dist/sources/introspect-supabase.js +4 -1
  244. package/dist/sources/rest.js +6 -2
  245. package/dist/studio/ui-components.generated.js +93 -1
  246. package/dist/upgrade-prompt.d.ts +1 -1
  247. package/dist/version.d.ts +2 -2
  248. package/dist/version.js +2 -2
  249. package/dist/wc/GridFooter-f5K0aLe_.js +145 -0
  250. package/dist/wc/GridMenus-FtSN_8t9.js +1369 -0
  251. package/dist/wc/PinSplitter-D-qtDhSM.js +132 -0
  252. package/dist/wc/{SvGrid.helpers-zBkDTryR.js → SvGrid.helpers-B2GEKrxS.js} +84 -36
  253. package/dist/wc/{SvGridCellEditor-CzUKEbkI.js → SvGridCellEditor-B_cLJrsp.js} +20 -9
  254. package/dist/wc/conditional-formatting-BXis_oP1.js +189 -0
  255. package/dist/wc/grid-extension.svelte-Bufq4B4C.js +20 -0
  256. package/dist/wc/sv-sheet-element.js +7556 -7617
  257. package/dist/wc/svelte-virtualizer.svelte-ehwOupl-.js +360 -0
  258. package/dist/wc/{validate-43C03DLP.js → validate-BaMO8UPE.js} +1 -1
  259. package/package.json +14 -3
  260. package/dist/designer/assets/GridMenus-CvcASlKv.js +0 -8
  261. package/dist/designer/assets/SvGrid.helpers-DE5mmORW.js +0 -1
  262. package/dist/designer/assets/SvGridCellEditor-C7NWcnNC.js +0 -4
  263. package/dist/designer/assets/SvListBox-BlmmzYcU.js +0 -110
  264. package/dist/designer/assets/index-KmSEY7Lb.css +0 -1
  265. package/dist/designer/assets/src-DqHCqori.js +0 -58
  266. package/dist/designer/assets/validate-B71dE1Cs.js +0 -1
  267. package/dist/wc/GridMenus-fWbBUiRH.js +0 -1088
  268. package/dist/wc/svelte-virtualizer.svelte-Dntdh8Ms.js +0 -581
@@ -0,0 +1,112 @@
1
+ <script lang="ts">
2
+ /**
3
+ * The view bar's "Color": no colors, the option colors of a single select,
4
+ * or conditions in order, each with its own color (the first match colors
5
+ * the row's header).
6
+ */
7
+ import { panelFocus } from './panel-focus'
8
+ import { filterKindOf, newCondition, operatorsFor, defaultValue, type FieldCondition, type FilterOp } from '../filter-model'
9
+ import { fieldColumns } from '../grid-access'
10
+ import { BRIGHT } from '../palette'
11
+ import type { ColorSetting } from '../view-model'
12
+ import ConditionValue from './ConditionValue.svelte'
13
+
14
+ let { ctrl, setting, onChange }: { ctrl: any; setting: ColorSetting; onChange: (s: ColorSetting) => void } = $props()
15
+
16
+ const selects = $derived(fieldColumns(ctrl).filter((c) => c.fieldType === 'singleSelect'))
17
+ const columns = $derived(fieldColumns(ctrl).filter((c) => filterKindOf(c.def)))
18
+ const rules = $derived(setting.mode === 'conditions' ? setting.rules : [])
19
+
20
+ function mode(m: ColorSetting['mode']) {
21
+ if (m === 'none') return onChange({ mode: 'none' })
22
+ if (m === 'field') return selects[0] ? onChange({ mode: 'field', field: selects[0].field }) : undefined
23
+ onChange({ mode: 'conditions', rules: rules.length ? rules : [] })
24
+ }
25
+ function setRules(next: Array<{ color: string; condition: FieldCondition }>) {
26
+ onChange({ mode: 'conditions', rules: next })
27
+ }
28
+ function addRule() {
29
+ const c = columns[0]
30
+ const condition = c && newCondition(c.id, c.def)
31
+ if (!condition) return
32
+ setRules([...rules, { color: BRIGHT[rules.length % BRIGHT.length]!.value, condition }])
33
+ }
34
+ const put = (i: number, r: { color: string; condition: FieldCondition } | null) =>
35
+ setRules(r ? rules.map((x, j) => (j === i ? r : x)) : rules.filter((_, j) => j !== i))
36
+ function changeOp(c: FieldCondition, op: FilterOp): FieldCondition {
37
+ return { ...c, op, value: defaultValue(c.kind, op) }
38
+ }
39
+ </script>
40
+
41
+ <div class="sv-fvb-panel sv-fvb-panel-wide sv-fcolor" use:panelFocus>
42
+ <div class="sv-fcolor-modes" role="radiogroup" aria-label="Color rows">
43
+ {#each [['none', 'None'], ['field', 'Select field'], ['conditions', 'Conditions']] as [m, label] (m)}
44
+ <button
45
+ type="button"
46
+ role="radio"
47
+ aria-checked={setting.mode === m}
48
+ class="sv-fcolor-mode"
49
+ class:is-on={setting.mode === m}
50
+ disabled={m === 'field' && !selects.length}
51
+ onclick={() => mode(m as ColorSetting['mode'])}>{label}</button
52
+ >
53
+ {/each}
54
+ </div>
55
+
56
+ {#if setting.mode === 'field'}
57
+ <p class="sv-fvb-title">Color records the same as a single select field</p>
58
+ <select class="sv-fvb-select" aria-label="Select field" value={setting.field} onchange={(e) => onChange({ mode: 'field', field: (e.currentTarget as HTMLSelectElement).value })}>
59
+ {#each selects as s (s.id)}<option value={s.field}>{s.header}</option>{/each}
60
+ </select>
61
+ {:else if setting.mode === 'conditions'}
62
+ <p class="sv-fvb-title">Color records that match a condition; the first match wins</p>
63
+ {#each rules as r, i (i)}
64
+ {@const col = columns.find((x) => x.id === r.condition.column)}
65
+ <div class="sv-fcolor-row">
66
+ <label class="sv-fcolor-swatch" style:background={r.color} title="Color">
67
+ <select aria-label="Color" value={r.color} onchange={(e) => put(i, { ...r, color: (e.currentTarget as HTMLSelectElement).value })}>
68
+ {#each BRIGHT as b (b.value)}<option value={b.value}>{b.label}</option>{/each}
69
+ </select>
70
+ </label>
71
+ <select
72
+ class="sv-fvb-select"
73
+ aria-label="Field"
74
+ value={r.condition.column}
75
+ onchange={(e) => {
76
+ const c = columns.find((x) => x.id === (e.currentTarget as HTMLSelectElement).value)
77
+ const condition = c && newCondition(c.id, c.def)
78
+ if (condition) put(i, { ...r, condition })
79
+ }}
80
+ >
81
+ {#each columns as x (x.id)}<option value={x.id}>{x.header}</option>{/each}
82
+ </select>
83
+ <select class="sv-fvb-select" aria-label="Operator" value={r.condition.op} onchange={(e) => put(i, { ...r, condition: changeOp(r.condition, (e.currentTarget as HTMLSelectElement).value as FilterOp) })}>
84
+ {#each operatorsFor(r.condition.kind) as o (o.op)}<option value={o.op}>{o.label}</option>{/each}
85
+ </select>
86
+ <span class="sv-fcolor-value">
87
+ {#if col}<ConditionValue column={col} condition={r.condition} onChange={(value) => put(i, { ...r, condition: { ...r.condition, value } })} />{/if}
88
+ </span>
89
+ <button type="button" class="sv-fvb-icon-btn" aria-label="Remove condition" onclick={() => put(i, null)}>
90
+ <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>
91
+ </button>
92
+ </div>
93
+ {/each}
94
+ <div class="sv-fvb-foot">
95
+ <button type="button" class="sv-fvb-text-btn" onclick={addRule}>+ Add condition</button>
96
+ </div>
97
+ {:else}
98
+ <p class="sv-fvb-empty">Rows are not colored</p>
99
+ {/if}
100
+ </div>
101
+
102
+ <style>
103
+ .sv-fcolor { width: 560px; max-width: 100%; }
104
+ .sv-fcolor-modes { display: inline-flex; border: 1px solid var(--sg-border, #e2e8f0); border-radius: 8px; padding: 2px; margin-bottom: 8px; }
105
+ .sv-fcolor-mode { border: 0; border-radius: 6px; padding: 3px 10px; background: none; color: inherit; font: inherit; cursor: pointer; }
106
+ .sv-fcolor-mode.is-on { background: color-mix(in srgb, var(--sg-accent, #2563eb) 14%, transparent); color: var(--sg-fg, #0f172a); }
107
+ .sv-fcolor-mode:disabled { opacity: 0.45; cursor: default; }
108
+ .sv-fcolor-row { display: grid; grid-template-columns: 24px 130px 120px minmax(0, 1fr) 28px; gap: 6px; align-items: start; margin: 6px 0; }
109
+ .sv-fcolor-row > :global(*) { min-width: 0; }
110
+ .sv-fcolor-swatch { position: relative; width: 22px; height: 22px; margin-top: 3px; border-radius: 6px; cursor: pointer; }
111
+ .sv-fcolor-swatch select { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
112
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { ColorSetting } from '../view-model.js';
2
+ type $$ComponentProps = {
3
+ ctrl: any;
4
+ setting: ColorSetting;
5
+ onChange: (s: ColorSetting) => void;
6
+ };
7
+ declare const ColorPanel: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type ColorPanel = ReturnType<typeof ColorPanel>;
9
+ export default ColorPanel;
@@ -0,0 +1,153 @@
1
+ <script lang="ts">
2
+ /**
3
+ * A filter condition's value, in the control its field and operator call
4
+ * for: a text box, a number, an option (or several, as pills), a person,
5
+ * checked / unchecked, a day relative to today or a date, a span of days.
6
+ */
7
+ import { ME, type DateSpan, type DateTarget, type FieldCondition } from '../filter-model'
8
+ import type { FieldColumn } from '../grid-access'
9
+ import { fieldPropsOf, originalColumn } from '../registry.svelte'
10
+ import { getFieldUsers } from '../sources.svelte'
11
+ import { textOn } from '../palette'
12
+
13
+ let { column, condition, onChange }: { column: FieldColumn; condition: FieldCondition; onChange: (value: unknown) => void } = $props()
14
+
15
+ type Choice = { value: string; label: string; color?: string }
16
+ const choices = $derived.by<Choice[]>(() => {
17
+ // "Me" first: whoever is signed in when the filter runs, so one view serves everyone.
18
+ if (column.fieldType === 'user' || column.fieldType === 'createdBy' || column.fieldType === 'lastModifiedBy')
19
+ return [{ value: ME, label: 'Me' }, ...getFieldUsers().map((u) => ({ value: u.id, label: u.name }))]
20
+ const p = fieldPropsOf(originalColumn(column.def)) as { options?: Array<{ value: string; color?: string }> }
21
+ return (p.options ?? []).map((o) => ({ value: o.value, label: o.value, ...(o.color ? { color: o.color } : {}) }))
22
+ })
23
+ const many = $derived(condition.kind === 'multi' || (condition.kind === 'select' && condition.op !== 'is' && condition.op !== 'isNot'))
24
+ const picked = $derived(new Set(Array.isArray(condition.value) ? (condition.value as string[]) : []))
25
+ function toggle(v: string) {
26
+ const next = new Set(picked)
27
+ if (next.has(v)) next.delete(v)
28
+ else next.add(v)
29
+ onChange([...next])
30
+ }
31
+
32
+ const DAYS: ReadonlyArray<{ value: DateTarget['mode']; label: string }> = [
33
+ { value: 'today', label: 'today' },
34
+ { value: 'tomorrow', label: 'tomorrow' },
35
+ { value: 'yesterday', label: 'yesterday' },
36
+ { value: 'oneWeekAgo', label: 'one week ago' },
37
+ { value: 'oneWeekFromNow', label: 'one week from now' },
38
+ { value: 'oneMonthAgo', label: 'one month ago' },
39
+ { value: 'oneMonthFromNow', label: 'one month from now' },
40
+ { value: 'exactDate', label: 'exact date' },
41
+ ]
42
+ const SPANS: ReadonlyArray<{ value: DateSpan['span']; label: string }> = [
43
+ { value: 'pastWeek', label: 'the past week' },
44
+ { value: 'pastMonth', label: 'the past month' },
45
+ { value: 'pastYear', label: 'the past year' },
46
+ { value: 'nextWeek', label: 'the next week' },
47
+ { value: 'nextMonth', label: 'the next month' },
48
+ { value: 'nextYear', label: 'the next year' },
49
+ { value: 'pastDays', label: 'the past number of days' },
50
+ { value: 'nextDays', label: 'the next number of days' },
51
+ ]
52
+ const target = $derived((condition.value ?? { mode: 'today' }) as DateTarget & { date?: string })
53
+ const span = $derived((condition.value ?? { span: 'pastWeek' }) as DateSpan)
54
+ const textValue = $derived(typeof condition.value === 'string' || typeof condition.value === 'number' ? String(condition.value) : '')
55
+ </script>
56
+
57
+ {#if condition.op === 'isEmpty' || condition.op === 'isNotEmpty'}
58
+ <span class="sv-fcv-none"></span>
59
+ {:else if condition.kind === 'checkbox'}
60
+ <label class="sv-fcv-check">
61
+ <input type="checkbox" checked={condition.value !== false} onchange={(e) => onChange((e.currentTarget as HTMLInputElement).checked)} />
62
+ {condition.value !== false ? 'checked' : 'unchecked'}
63
+ </label>
64
+ {:else if condition.kind === 'select' && !many}
65
+ <select class="sv-fvb-select" aria-label="Value" value={String(condition.value ?? '')} onchange={(e) => onChange((e.currentTarget as HTMLSelectElement).value)}>
66
+ <option value="" disabled>Select an option</option>
67
+ {#each choices as c (c.value)}<option value={c.value}>{c.label}</option>{/each}
68
+ </select>
69
+ {:else if many}
70
+ <div class="sv-fcv-pills" role="group" aria-label="Values">
71
+ {#each choices as c (c.value)}
72
+ <button
73
+ type="button"
74
+ class="sv-fcv-pill"
75
+ class:is-on={picked.has(c.value)}
76
+ aria-pressed={picked.has(c.value)}
77
+ style:--pill={c.color ?? 'var(--sg-accent, #2563eb)'}
78
+ style:--pill-fg={c.color ? textOn(c.color) : '#fff'}
79
+ onclick={() => toggle(c.value)}>{c.label}</button
80
+ >
81
+ {:else}
82
+ <span class="sv-fcv-empty">No options</span>
83
+ {/each}
84
+ </div>
85
+ {:else if condition.kind === 'date' && condition.op === 'isWithin'}
86
+ <span class="sv-fcv-pair">
87
+ <select
88
+ class="sv-fvb-select"
89
+ aria-label="Span"
90
+ value={span.span}
91
+ onchange={(e) => onChange({ span: (e.currentTarget as HTMLSelectElement).value, ...(span.days ? { days: span.days } : {}) })}
92
+ >
93
+ {#each SPANS as s (s.value)}<option value={s.value}>{s.label}</option>{/each}
94
+ </select>
95
+ {#if span.span === 'pastDays' || span.span === 'nextDays'}
96
+ <input
97
+ class="sv-fvb-input sv-fcv-days"
98
+ type="number"
99
+ min="1"
100
+ aria-label="Days"
101
+ value={span.days ?? ''}
102
+ oninput={(e) => onChange({ span: span.span, days: Number((e.currentTarget as HTMLInputElement).value) })}
103
+ />
104
+ {/if}
105
+ </span>
106
+ {:else if condition.kind === 'date'}
107
+ <span class="sv-fcv-pair">
108
+ <select
109
+ class="sv-fvb-select"
110
+ aria-label="Day"
111
+ value={target.mode}
112
+ onchange={(e) => {
113
+ const mode = (e.currentTarget as HTMLSelectElement).value
114
+ onChange(mode === 'exactDate' ? { mode, date: target.date ?? '' } : { mode })
115
+ }}
116
+ >
117
+ {#each DAYS as d (d.value)}<option value={d.value}>{d.label}</option>{/each}
118
+ </select>
119
+ {#if target.mode === 'exactDate'}
120
+ <input
121
+ class="sv-fvb-input"
122
+ type="date"
123
+ aria-label="Date"
124
+ value={target.date ?? ''}
125
+ oninput={(e) => onChange({ mode: 'exactDate', date: (e.currentTarget as HTMLInputElement).value })}
126
+ />
127
+ {/if}
128
+ </span>
129
+ {:else}
130
+ <input
131
+ class="sv-fvb-input"
132
+ type="text"
133
+ inputmode={condition.kind === 'number' ? 'decimal' : undefined}
134
+ placeholder="Enter a value"
135
+ aria-label="Value"
136
+ value={textValue}
137
+ oninput={(e) => onChange((e.currentTarget as HTMLInputElement).value)}
138
+ />
139
+ {/if}
140
+
141
+ <style>
142
+ .sv-fcv-none { display: block; }
143
+ .sv-fcv-check { display: inline-flex; align-items: center; gap: 6px; }
144
+ .sv-fcv-pair { display: flex; gap: 6px; min-width: 0; }
145
+ .sv-fcv-days { width: 70px; }
146
+ .sv-fcv-pills { display: flex; flex-wrap: wrap; gap: 4px; }
147
+ .sv-fcv-pill {
148
+ border: 1px solid var(--sg-border, #cbd5e1); border-radius: 999px; padding: 1px 9px; font: inherit; font-size: 12px;
149
+ background: transparent; color: inherit; cursor: pointer;
150
+ }
151
+ .sv-fcv-pill.is-on { background: var(--pill); border-color: var(--pill); color: var(--pill-fg); }
152
+ .sv-fcv-empty { color: var(--sg-muted, #64748b); font-size: 12px; }
153
+ </style>
@@ -0,0 +1,15 @@
1
+ /**
2
+ * A filter condition's value, in the control its field and operator call
3
+ * for: a text box, a number, an option (or several, as pills), a person,
4
+ * checked / unchecked, a day relative to today or a date, a span of days.
5
+ */
6
+ import { type FieldCondition } from '../filter-model.js';
7
+ import type { FieldColumn } from '../grid-access.js';
8
+ type $$ComponentProps = {
9
+ column: FieldColumn;
10
+ condition: FieldCondition;
11
+ onChange: (value: unknown) => void;
12
+ };
13
+ declare const ConditionValue: import("svelte").Component<$$ComponentProps, {}, "">;
14
+ type ConditionValue = ReturnType<typeof ConditionValue>;
15
+ export default ConditionValue;
@@ -0,0 +1,18 @@
1
+ <script lang="ts">
2
+ /** A field's type icon, as the header and the field editor show it. */
3
+ import { getFieldType } from '../registry.svelte'
4
+
5
+ let { column, size = 14 }: { column: { fieldType?: string }; size?: number } = $props()
6
+
7
+ // A column without a field type: a plain "text" mark.
8
+ const TEXT = 'M5 7h14M5 12h10M5 17h7'
9
+ const path = $derived((column.fieldType && getFieldType(column.fieldType)?.icon) || TEXT)
10
+ </script>
11
+
12
+ <svg class="sv-fvb-field-icon" viewBox="0 0 24 24" width={size} height={size} aria-hidden="true"
13
+ ><path d={path} fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" /></svg
14
+ >
15
+
16
+ <style>
17
+ .sv-fvb-field-icon { flex: none; color: var(--sg-muted, #64748b); }
18
+ </style>
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ column: {
3
+ fieldType?: string;
4
+ };
5
+ size?: number;
6
+ };
7
+ declare const FieldIcon: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type FieldIcon = ReturnType<typeof FieldIcon>;
9
+ export default FieldIcon;
@@ -0,0 +1,192 @@
1
+ <script lang="ts">
2
+ /**
3
+ * The view bar's "Filter": "Where [field] [operator] [value]" rows joined
4
+ * by "and" or "or", and condition groups one level deep. Each change
5
+ * filters the grid at once; a condition without a value yet is left out.
6
+ *
7
+ * With `onChange` it edits `value` instead of the grid's filter: an alert
8
+ * rule's condition is written with it, inside the rule's dialog.
9
+ */
10
+ import { panelFocus } from './panel-focus'
11
+ import {
12
+ defaultValue,
13
+ filterKindOf,
14
+ isGroup,
15
+ newCondition,
16
+ operatorsFor,
17
+ type FieldCondition,
18
+ type FieldConditionGroup,
19
+ type FieldFilter,
20
+ type FilterOp,
21
+ } from '../filter-model'
22
+ import { fieldColumns } from '../grid-access'
23
+ import ConditionValue from './ConditionValue.svelte'
24
+
25
+ let {
26
+ ctrl,
27
+ api,
28
+ value,
29
+ onChange,
30
+ title = 'In this view, show records',
31
+ empty = 'No filter conditions are applied to this view',
32
+ }: {
33
+ ctrl: any
34
+ api?: any
35
+ /** With `onChange`: the filter shown, in place of the grid's. */
36
+ value?: FieldFilter | null
37
+ onChange?: (filter: FieldFilter) => void
38
+ title?: string
39
+ empty?: string
40
+ } = $props()
41
+
42
+ const columns = $derived(fieldColumns(ctrl).filter((c) => filterKindOf(c.def)))
43
+ const filter = $derived.by<FieldFilter>(() => {
44
+ const f = (onChange ? value : ctrl.advancedFilter) as FieldFilter | null | undefined
45
+ return f?.kind === 'fieldFilter' ? f : { kind: 'fieldFilter', conjunction: 'and', conditions: [] }
46
+ })
47
+
48
+ function save(next: FieldFilter) {
49
+ if (onChange) return onChange(next)
50
+ if (next.conditions.length) api.setAdvancedFilter(next)
51
+ else api.clearAdvancedFilter()
52
+ }
53
+ const first = () => columns[0]
54
+ function addCondition(groupAt?: number) {
55
+ const c = first()
56
+ if (!c) return
57
+ const cond = newCondition(c.id, c.def)
58
+ if (!cond) return
59
+ const conditions = [...filter.conditions]
60
+ if (groupAt == null) conditions.push(cond)
61
+ else {
62
+ const g = conditions[groupAt] as FieldConditionGroup
63
+ conditions[groupAt] = { ...g, conditions: [...g.conditions, cond] }
64
+ }
65
+ save({ ...filter, conditions })
66
+ }
67
+ function addGroup() {
68
+ const c = first()
69
+ const cond = c && newCondition(c.id, c.def)
70
+ if (!cond) return
71
+ save({ ...filter, conditions: [...filter.conditions, { conjunction: 'and', conditions: [cond] }] })
72
+ }
73
+ /** Replace (or with null remove) a condition, at the top level or in a group. */
74
+ function put(at: number, inner: number | null, next: FieldCondition | null) {
75
+ const conditions = [...filter.conditions]
76
+ if (inner == null) {
77
+ if (next) conditions[at] = next
78
+ else conditions.splice(at, 1)
79
+ } else {
80
+ const g = conditions[at] as FieldConditionGroup
81
+ const list = [...g.conditions]
82
+ if (next) list[inner] = next
83
+ else list.splice(inner, 1)
84
+ if (list.length) conditions[at] = { ...g, conditions: list }
85
+ else conditions.splice(at, 1)
86
+ }
87
+ save({ ...filter, conditions })
88
+ }
89
+ function setConjunction(at: number | null, conj: 'and' | 'or') {
90
+ if (at == null) return save({ ...filter, conjunction: conj })
91
+ const conditions = [...filter.conditions]
92
+ conditions[at] = { ...(conditions[at] as FieldConditionGroup), conjunction: conj }
93
+ save({ ...filter, conditions })
94
+ }
95
+
96
+ // A panel in a popover takes focus when it opens; one inside a dialog does not.
97
+ function takeFocus(node: HTMLElement, on: boolean) {
98
+ return on ? panelFocus(node) : undefined
99
+ }
100
+
101
+ function changeField(c: FieldCondition, columnId: string): FieldCondition {
102
+ const col = columns.find((x) => x.id === columnId)
103
+ return (col && newCondition(col.id, col.def)) || c
104
+ }
105
+ /** A new operator keeps the value when it takes the same shape of value. */
106
+ function changeOp(c: FieldCondition, op: FilterOp): FieldCondition {
107
+ const shape = (o: FilterOp) =>
108
+ c.kind === 'date' ? (o === 'isWithin' ? 'span' : 'day') : c.kind === 'select' ? (o === 'is' || o === 'isNot' ? 'one' : 'many') : 'same'
109
+ const keep = shape(op) === shape(c.op) && op !== 'isEmpty' && op !== 'isNotEmpty'
110
+ return { ...c, op, value: keep && c.value !== undefined ? c.value : defaultValue(c.kind, op) }
111
+ }
112
+ </script>
113
+
114
+ {#snippet row(c: FieldCondition, index: number, conj: 'and' | 'or', setConj: (v: 'and' | 'or') => void, at: number, inner: number | null)}
115
+ {@const col = columns.find((x) => x.id === c.column)}
116
+ <div class="sv-ff-row">
117
+ <span class="sv-ff-conj">
118
+ {#if index === 0}
119
+ Where
120
+ {:else if index === 1}
121
+ <select class="sv-fvb-select" aria-label="Join conditions with" value={conj} onchange={(e) => setConj((e.currentTarget as HTMLSelectElement).value as 'and' | 'or')}>
122
+ <option value="and">and</option>
123
+ <option value="or">or</option>
124
+ </select>
125
+ {:else}
126
+ {conj}
127
+ {/if}
128
+ </span>
129
+ <select class="sv-fvb-select" aria-label="Field" value={c.column} onchange={(e) => put(at, inner, changeField(c, (e.currentTarget as HTMLSelectElement).value))}>
130
+ {#each columns as x (x.id)}<option value={x.id}>{x.header}</option>{/each}
131
+ </select>
132
+ <select class="sv-fvb-select" aria-label="Operator" value={c.op} onchange={(e) => put(at, inner, changeOp(c, (e.currentTarget as HTMLSelectElement).value as FilterOp))}>
133
+ {#each operatorsFor(c.kind) as o (o.op)}<option value={o.op}>{o.label}</option>{/each}
134
+ </select>
135
+ <span class="sv-ff-value">
136
+ {#if col}<ConditionValue column={col} condition={c} onChange={(value) => put(at, inner, { ...c, value })} />{/if}
137
+ </span>
138
+ <button type="button" class="sv-fvb-icon-btn" aria-label="Remove condition" title="Remove condition" onclick={() => put(at, inner, null)}>
139
+ <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" /></svg>
140
+ </button>
141
+ </div>
142
+ {/snippet}
143
+
144
+ <div class="sv-fvb-panel sv-fvb-panel-wide sv-ff" class:is-inline={!!onChange} use:takeFocus={!onChange}>
145
+ {#if filter.conditions.length}
146
+ {#if title}<p class="sv-fvb-title">{title}</p>{/if}
147
+ {#each filter.conditions as c, i (i)}
148
+ {#if isGroup(c)}
149
+ {@const g = c}
150
+ <div class="sv-ff-row">
151
+ <span class="sv-ff-conj">
152
+ {#if i === 0}Where{:else if i === 1}
153
+ <select class="sv-fvb-select" aria-label="Join conditions with" value={filter.conjunction} onchange={(e) => setConjunction(null, (e.currentTarget as HTMLSelectElement).value as 'and' | 'or')}>
154
+ <option value="and">and</option>
155
+ <option value="or">or</option>
156
+ </select>
157
+ {:else}{filter.conjunction}{/if}
158
+ </span>
159
+ <div class="sv-ff-group">
160
+ {#each g.conditions as inner, j (j)}
161
+ {@render row(inner, j, g.conjunction, (v) => setConjunction(i, v), i, j)}
162
+ {/each}
163
+ <button type="button" class="sv-fvb-text-btn" onclick={() => addCondition(i)}>+ Add condition</button>
164
+ </div>
165
+ </div>
166
+ {:else}
167
+ {@render row(c, i, filter.conjunction, (v) => setConjunction(null, v), i, null)}
168
+ {/if}
169
+ {/each}
170
+ {:else}
171
+ <p class="sv-fvb-empty">{empty}</p>
172
+ {/if}
173
+ <div class="sv-fvb-foot">
174
+ <button type="button" class="sv-fvb-text-btn" onclick={() => addCondition()}>+ Add condition</button>
175
+ <button type="button" class="sv-fvb-text-btn" onclick={addGroup}>+ Add condition group</button>
176
+ </div>
177
+ </div>
178
+
179
+ <style>
180
+ .sv-ff { width: 700px; max-width: 100%; }
181
+ .sv-ff.is-inline { width: auto; }
182
+ .sv-ff-row { display: grid; grid-template-columns: 64px 140px 130px minmax(0, 1fr) 28px; gap: 6px; align-items: start; margin: 6px 0; }
183
+ .sv-ff-row > :global(*) { min-width: 0; }
184
+ .sv-ff-conj { padding-top: 5px; color: var(--sg-muted, #64748b); }
185
+ .sv-ff-value { min-width: 0; }
186
+ .sv-ff-group {
187
+ grid-column: 2 / -1; padding: 4px 8px; border-radius: 8px;
188
+ background: color-mix(in srgb, var(--sg-fg, #0f172a) 4%, transparent);
189
+ border: 1px solid var(--sg-border, #e2e8f0);
190
+ }
191
+ .sv-ff-group .sv-ff-row { grid-template-columns: 56px 120px 120px minmax(0, 1fr) 28px; }
192
+ </style>
@@ -0,0 +1,13 @@
1
+ import { type FieldFilter } from '../filter-model.js';
2
+ type $$ComponentProps = {
3
+ ctrl: any;
4
+ api?: any;
5
+ /** With `onChange`: the filter shown, in place of the grid's. */
6
+ value?: FieldFilter | null;
7
+ onChange?: (filter: FieldFilter) => void;
8
+ title?: string;
9
+ empty?: string;
10
+ };
11
+ declare const FilterPanel: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type FilterPanel = ReturnType<typeof FilterPanel>;
13
+ export default FilterPanel;
@@ -0,0 +1,60 @@
1
+ <script lang="ts">
2
+ /**
3
+ * "Customize timeline": the fields a Gantt view draws with - when a record
4
+ * starts and ends, the percent field its bar fills with, and the link field
5
+ * naming the records it waits for (drawn as arrows). Each change shows at
6
+ * once and is kept in the view.
7
+ */
8
+ import { panelFocus } from './panel-focus'
9
+ import { dateColumns, dependencyColumns, progressColumns, type FieldView } from '../view-model'
10
+
11
+ type Settings = Pick<FieldView, 'dateField' | 'endField' | 'progressField' | 'dependencyField'>
12
+ let { ctrl, view, onChange }: { ctrl: any; view: Settings; onChange: (next: Settings) => void } = $props()
13
+
14
+ const dates = $derived(dateColumns(ctrl))
15
+ const progress = $derived(progressColumns(ctrl))
16
+ const deps = $derived(dependencyColumns(ctrl))
17
+ const valueOf = (e: Event) => (e.currentTarget as HTMLSelectElement).value || undefined
18
+ const set = (key: keyof Settings, e: Event) => onChange({ ...view, [key]: valueOf(e) })
19
+ </script>
20
+
21
+ <div class="sv-fvb-panel sv-fgantt-panel" use:panelFocus>
22
+ <p class="sv-fvb-title">Timeline</p>
23
+ <label class="sv-fgantt-field">
24
+ <span>Start date</span>
25
+ <select class="sv-fvb-select" value={view.dateField} onchange={(e) => set('dateField', e)}>
26
+ {#each dates as c (c.id)}<option value={c.id}>{c.header}</option>{/each}
27
+ </select>
28
+ </label>
29
+ <label class="sv-fgantt-field">
30
+ <span>End date</span>
31
+ <select class="sv-fvb-select" value={view.endField ?? ''} onchange={(e) => set('endField', e)}>
32
+ <option value="">None (a milestone)</option>
33
+ {#each dates.filter((c) => c.id !== view.dateField) as c (c.id)}<option value={c.id}>{c.header}</option>{/each}
34
+ </select>
35
+ </label>
36
+ <label class="sv-fgantt-field">
37
+ <span>Progress</span>
38
+ <select class="sv-fvb-select" value={view.progressField ?? ''} onchange={(e) => set('progressField', e)} disabled={!progress.length}>
39
+ <option value="">None</option>
40
+ {#each progress as c (c.id)}<option value={c.id}>{c.header}</option>{/each}
41
+ </select>
42
+ </label>
43
+ <label class="sv-fgantt-field">
44
+ <span>Depends on</span>
45
+ <select class="sv-fvb-select" value={view.dependencyField ?? ''} onchange={(e) => set('dependencyField', e)} disabled={!deps.length}>
46
+ <option value="">None</option>
47
+ {#each deps as c (c.id)}<option value={c.id}>{c.header}</option>{/each}
48
+ </select>
49
+ </label>
50
+ {#if !deps.length}
51
+ <p class="sv-fgantt-hint">Arrows between records come from a link field to this same table.</p>
52
+ {/if}
53
+ </div>
54
+
55
+ <style>
56
+ .sv-fgantt-panel { display: flex; flex-direction: column; gap: 10px; min-width: 280px; }
57
+ .sv-fgantt-field { display: grid; grid-template-columns: 96px 1fr; align-items: center; gap: 8px; font-size: 13px; }
58
+ .sv-fgantt-field > span { color: var(--sg-muted, #64748b); }
59
+ .sv-fgantt-hint { margin: 0; max-width: 280px; font-size: 12px; color: var(--sg-muted, #64748b); }
60
+ </style>
@@ -0,0 +1,10 @@
1
+ import { type FieldView } from '../view-model.js';
2
+ type Settings = Pick<FieldView, 'dateField' | 'endField' | 'progressField' | 'dependencyField'>;
3
+ type $$ComponentProps = {
4
+ ctrl: any;
5
+ view: Settings;
6
+ onChange: (next: Settings) => void;
7
+ };
8
+ declare const GanttPanel: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type GanttPanel = ReturnType<typeof GanttPanel>;
10
+ export default GanttPanel;