@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.
Files changed (270) 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 +11375 -10805
  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-DtBIaQno.js → index-BPU2KNc5.js} +396 -395
  34. package/dist/designer/assets/index-BmvG99kR.css +1 -0
  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 +141 -20
  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/emit-project.js +25 -6
  246. package/dist/studio/sveltekit3.js +13 -7
  247. package/dist/studio/ui-components.generated.js +94 -2
  248. package/dist/upgrade-prompt.d.ts +1 -1
  249. package/dist/version.d.ts +2 -2
  250. package/dist/version.js +2 -2
  251. package/dist/wc/GridFooter-f5K0aLe_.js +145 -0
  252. package/dist/wc/GridMenus-FtSN_8t9.js +1369 -0
  253. package/dist/wc/PinSplitter-D-qtDhSM.js +132 -0
  254. package/dist/wc/{SvGrid.helpers-zBkDTryR.js → SvGrid.helpers-B2GEKrxS.js} +84 -36
  255. package/dist/wc/{SvGridCellEditor-CzUKEbkI.js → SvGridCellEditor-B_cLJrsp.js} +20 -9
  256. package/dist/wc/conditional-formatting-BXis_oP1.js +189 -0
  257. package/dist/wc/grid-extension.svelte-Bufq4B4C.js +20 -0
  258. package/dist/wc/sv-sheet-element.js +7561 -7618
  259. package/dist/wc/svelte-virtualizer.svelte-ehwOupl-.js +360 -0
  260. package/dist/wc/{validate-43C03DLP.js → validate-BaMO8UPE.js} +1 -1
  261. package/package.json +14 -3
  262. package/dist/designer/assets/GridMenus-CvcASlKv.js +0 -8
  263. package/dist/designer/assets/SvGrid.helpers-DE5mmORW.js +0 -1
  264. package/dist/designer/assets/SvGridCellEditor-C7NWcnNC.js +0 -4
  265. package/dist/designer/assets/SvListBox-BlmmzYcU.js +0 -110
  266. package/dist/designer/assets/index-KmSEY7Lb.css +0 -1
  267. package/dist/designer/assets/src-DqHCqori.js +0 -58
  268. package/dist/designer/assets/validate-B71dE1Cs.js +0 -1
  269. package/dist/wc/GridMenus-fWbBUiRH.js +0 -1088
  270. package/dist/wc/svelte-virtualizer.svelte-Dntdh8Ms.js +0 -581
@@ -0,0 +1,295 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvBaseForm - a form that adds a record to the table: a title, a line
4
+ * under it, then each field with its name and the editor its cell uses
5
+ * (the select panel, the number box, the date picker, the file dialog).
6
+ * The fields start with the defaults a new record gets. Submit checks
7
+ * required fields, email / URL / phone formats and each column's
8
+ * `validate`, adds the row the way "+ Add row" does (field defaults, the
9
+ * next autonumber, `onRowAdd`), then offers to fill in another. A select
10
+ * offers only the field's options: a form never adds one.
11
+ */
12
+ import { untrack } from 'svelte'
13
+ import { parseEditorValue, type CellEditorType } from '@svgrid/grid'
14
+ import { fieldColumns, gridOf, primaryIdOf, type FieldColumn } from './grid-access'
15
+ import { newRowValues } from './field-ops'
16
+ import { linkHref } from './links'
17
+ import FieldInput from './FieldInput.svelte'
18
+ import FieldIcon from './viewbar/FieldIcon.svelte'
19
+
20
+ let {
21
+ api,
22
+ fields,
23
+ required = [],
24
+ title = 'New record',
25
+ description = '',
26
+ submitLabel = 'Submit',
27
+ defaults,
28
+ onSubmit,
29
+ }: {
30
+ /** The grid's api. */
31
+ api: any
32
+ /** The fields asked for, by column id, in order. Default: every field a person can fill in. */
33
+ fields?: ReadonlyArray<string>
34
+ /** Fields that must be filled in before Submit. */
35
+ required?: ReadonlyArray<string>
36
+ title?: string
37
+ description?: string
38
+ submitLabel?: string
39
+ /** Values each record from this form starts with, under the row adder's `defaults` (`<SvBase>` gives an id). */
40
+ defaults?: () => Record<string, unknown>
41
+ /**
42
+ * After the row was added. Return a Promise to wait on it: the button is
43
+ * busy until it settles, and a rejection takes the row back out of the
44
+ * grid, keeps what was typed and shows the error's message.
45
+ */
46
+ onSubmit?: (row: Record<string, unknown>) => void | Promise<unknown>
47
+ } = $props()
48
+
49
+ // Computed by the table, never typed in.
50
+ const COMPUTED = new Set(['formula', 'lookup', 'rollup', 'count', 'autoNumber', 'createdTime', 'lastModifiedTime', 'createdBy', 'lastModifiedBy', 'button'])
51
+ const ctrl = $derived(api ? gridOf(api) : undefined)
52
+ const columns = $derived.by<FieldColumn[]>(() => {
53
+ if (!ctrl) return []
54
+ const all = fieldColumns(ctrl).filter((c) => !COMPUTED.has(c.fieldType ?? '') && c.def.editable !== false && !c.def.fieldFn)
55
+ if (!fields) {
56
+ // The primary field first, as the record's name.
57
+ const p = primaryIdOf(ctrl)
58
+ return [...all.filter((c) => c.id === p), ...all.filter((c) => c.id !== p)]
59
+ }
60
+ return fields.map((id) => all.find((c) => c.id === id)).filter((c): c is FieldColumn => !!c)
61
+ })
62
+
63
+ /** What an empty field says it wants. */
64
+ function placeholderFor(c: FieldColumn): string {
65
+ const t = c.fieldType ?? ''
66
+ if (t === 'singleSelect' || t === 'multiSelect' || t === 'user') return 'Choose'
67
+ if (t === 'date') return 'Pick a date'
68
+ if (t === 'attachment') return 'Add files'
69
+ if (t === 'link') return 'Link records'
70
+ return 'Type here'
71
+ }
72
+
73
+ let draft = $state<Record<string, unknown>>({})
74
+ let editing = $state<string | null>(null)
75
+ // Column id -> what is wrong with its value, shown under the field.
76
+ let errors = $state<Record<string, string>>({})
77
+ let sent = $state(false)
78
+ let busy = $state(false)
79
+ let failure = $state('')
80
+ // The values the record starts with (row adder defaults, the form's own),
81
+ // read once per record so an id made there is the record's id.
82
+ let seed: Record<string, unknown> | null = null
83
+ // Field -> the default it was filled with.
84
+ let prefilled: Record<string, unknown> = {}
85
+
86
+ const isBlank = (v: unknown) => v == null || v === '' || (Array.isArray(v) && v.length === 0)
87
+
88
+ /** A fresh record: the defaults it would get, shown in the fields asked for. */
89
+ function prefill() {
90
+ if (!ctrl) return
91
+ const cfg = ctrl.props.rowAdder
92
+ const fromAdder = cfg && typeof cfg === 'object' && typeof cfg.defaults === 'function' ? cfg.defaults() : {}
93
+ seed = { ...(defaults?.() ?? {}), ...fromAdder }
94
+ const start = newRowValues(ctrl, seed)
95
+ const next: Record<string, unknown> = {}
96
+ for (const c of columns) {
97
+ const v = start[c.field]
98
+ if (v !== undefined && !isBlank(v)) next[c.field] = Array.isArray(v) ? v.slice() : v
99
+ }
100
+ prefilled = { ...next }
101
+ draft = next
102
+ }
103
+ // Once the grid is there; a remount (another form view) starts again.
104
+ $effect.pre(() => {
105
+ if (ctrl && !seed) untrack(prefill)
106
+ })
107
+ /** A date picked for a column that keeps dates as text keeps it as 'YYYY-MM-DD'. */
108
+ function stored(c: FieldColumn, v: unknown): unknown {
109
+ if (v instanceof Date && c.def.cellDataType === 'dateString') {
110
+ const p = (n: number) => String(n).padStart(2, '0')
111
+ return `${v.getFullYear()}-${p(v.getMonth() + 1)}-${p(v.getDate())}`
112
+ }
113
+ return v
114
+ }
115
+ /**
116
+ * The editor's value as the grid would store it from a cell edit: coerced
117
+ * for the editor type, then through the column's valueParser, so a custom
118
+ * field type parses the same in a form.
119
+ */
120
+ function parsed(c: FieldColumn, raw: unknown): unknown {
121
+ const def = c.def as Record<string, any>
122
+ const value = parseEditorValue(String(def.editorType ?? 'text') as CellEditorType, stored(c, raw), {
123
+ multiple: def.editorMultiple === true,
124
+ dateOnly: def.cellDataType === 'dateString',
125
+ })
126
+ const parser = def.valueParser as ((p: Record<string, unknown>) => unknown) | undefined
127
+ return parser ? parser({ newValue: value, oldValue: draft[c.field], rawInput: raw, data: draft, columnId: c.id }) : value
128
+ }
129
+ function set(c: FieldColumn, v: unknown) {
130
+ draft = { ...draft, [c.field]: parsed(c, v) }
131
+ if (errors[c.id]) {
132
+ const rest = { ...errors }
133
+ delete rest[c.id]
134
+ errors = rest
135
+ }
136
+ }
137
+ const FORMATS: Record<string, { kind: 'email' | 'url' | 'phone'; message: string }> = {
138
+ email: { kind: 'email', message: 'Not a valid email address' },
139
+ url: { kind: 'url', message: 'Not a valid web address' },
140
+ phone: { kind: 'phone', message: 'Not a valid phone number' },
141
+ }
142
+ /** What is wrong with a field's value, or '' when nothing is. */
143
+ function problem(c: FieldColumn, v: unknown): string {
144
+ const blank = isBlank(v)
145
+ if (blank && required.includes(c.id)) return `${c.header} is required`
146
+ // The format its cell links by: mailto, https, tel.
147
+ const format = FORMATS[c.fieldType ?? '']
148
+ if (!blank && format && !linkHref(format.kind, v)) return format.message
149
+ // The column's own rule, as the grid runs it on a cell.
150
+ const validate = c.def.validate as ((p: Record<string, unknown>) => unknown) | undefined
151
+ if (typeof validate === 'function') {
152
+ const verdict = validate({ value: v, row: draft, rowIndex: -1, column: { id: c.id, columnDef: c.def } })
153
+ if (verdict === false) return `${c.header} is not valid`
154
+ if (typeof verdict === 'string' && verdict) return verdict
155
+ }
156
+ return ''
157
+ }
158
+ function editBeside(c: FieldColumn, dir: 1 | -1) {
159
+ const at = columns.findIndex((x) => x.id === c.id)
160
+ const next = columns[at + dir]
161
+ editing = next && next.def.editorType !== 'checkbox' ? next.id : null
162
+ }
163
+ /**
164
+ * What the form sends: what the fields show. A field left empty is left
165
+ * out, so it gets no value; one whose default was cleared stays empty.
166
+ */
167
+ function given(): Record<string, unknown> {
168
+ const out: Record<string, unknown> = {}
169
+ for (const [field, v] of Object.entries(draft)) {
170
+ if (!isBlank(v) || field in prefilled) out[field] = v
171
+ }
172
+ return out
173
+ }
174
+ async function submit(e: SubmitEvent) {
175
+ e.preventDefault()
176
+ if (!ctrl || busy) return
177
+ editing = null
178
+ failure = ''
179
+ const found: Record<string, string> = {}
180
+ for (const c of columns) {
181
+ const why = problem(c, draft[c.field])
182
+ if (why) found[c.id] = why
183
+ }
184
+ errors = found
185
+ if (Object.keys(found).length) return
186
+ if (!seed) prefill()
187
+ // Field defaults, the next autonumber, the created time; then what the fields show.
188
+ const row: Record<string, unknown> = newRowValues(ctrl, { ...seed, ...given() })
189
+ api.addRow(row, 'bottom')
190
+ ctrl.props.onRowAdd?.({ row })
191
+ let result: unknown
192
+ try {
193
+ result = onSubmit?.(row)
194
+ } catch (err) {
195
+ return fail(row, err)
196
+ }
197
+ if (result && typeof (result as Promise<unknown>).then === 'function') {
198
+ busy = true
199
+ try {
200
+ await result
201
+ } catch (err) {
202
+ return fail(row, err)
203
+ } finally {
204
+ busy = false
205
+ }
206
+ }
207
+ sent = true
208
+ }
209
+ /** onSubmit failed: the row comes out of the grid again, the fields keep their values. */
210
+ function fail(row: Record<string, unknown>, err: unknown) {
211
+ const at = ctrl ? ctrl.internalData.indexOf(row) : -1
212
+ if (at >= 0) api.removeRows([at])
213
+ failure = err instanceof Error && err.message ? err.message : 'The record could not be sent. Try again.'
214
+ }
215
+ function again() {
216
+ seed = null
217
+ errors = {}
218
+ failure = ''
219
+ prefill()
220
+ sent = false
221
+ }
222
+ </script>
223
+
224
+ {#if ctrl}
225
+ <div class="sv-fform-wrap">
226
+ {#if sent}
227
+ <div class="sv-fform sv-fform-done" role="status">
228
+ <h2 class="sv-fform-title">Thank you</h2>
229
+ <p class="sv-fform-desc">The record was added.</p>
230
+ <button type="button" class="sv-fform-submit" onclick={again}>Submit another</button>
231
+ </div>
232
+ {:else}
233
+ <form class="sv-fform" onsubmit={submit} novalidate>
234
+ <h2 class="sv-fform-title">{title}</h2>
235
+ {#if description}<p class="sv-fform-desc">{description}</p>{/if}
236
+ {#each columns as c (c.id)}
237
+ {@const v = draft[c.field]}
238
+ <div class="sv-fform-field" class:is-missing={!!errors[c.id]}>
239
+ <div class="sv-fform-label">
240
+ <FieldIcon column={c} />
241
+ <span>{c.header}</span>
242
+ {#if required.includes(c.id)}<span class="sv-fform-req" aria-label="required">*</span>{/if}
243
+ </div>
244
+ <FieldInput
245
+ {ctrl}
246
+ column={{ id: c.id, columnDef: c.def }}
247
+ value={v}
248
+ row={{ id: '', original: draft }}
249
+ label={c.header}
250
+ editable
251
+ editing={editing === c.id}
252
+ placeholder={placeholderFor(c)}
253
+ onStart={() => (editing = c.id)}
254
+ onStop={() => (editing = null)}
255
+ onCommit={(next) => set(c, next)}
256
+ onTab={(dir) => editBeside(c, dir)}
257
+ addOptions={false}
258
+ />
259
+ {#if errors[c.id]}<p class="sv-fform-error">{errors[c.id]}</p>{/if}
260
+ {#if c.def.description}<p class="sv-fform-hint">{String(c.def.description)}</p>{/if}
261
+ </div>
262
+ {/each}
263
+ {#if failure}<p class="sv-fform-error sv-fform-failure" role="alert">{failure}</p>{/if}
264
+ <button type="submit" class="sv-fform-submit" disabled={busy} aria-busy={busy}>{submitLabel}</button>
265
+ </form>
266
+ {/if}
267
+ </div>
268
+ {/if}
269
+
270
+ <style>
271
+ .sv-fform-wrap {
272
+ height: 100%; overflow: auto; box-sizing: border-box; padding: 24px 16px;
273
+ background: color-mix(in srgb, var(--sg-fg, #0f172a) 4%, var(--sg-bg, #fff));
274
+ font-family: var(--sg-font, inherit); color: var(--sg-fg, #0f172a);
275
+ }
276
+ .sv-fform {
277
+ max-width: 640px; margin: 0 auto; padding: 24px 28px; display: flex; flex-direction: column; gap: 18px;
278
+ background: var(--sg-bg, #fff); border: 1px solid var(--sg-border, #e2e8f0); border-radius: 12px;
279
+ }
280
+ .sv-fform-title { margin: 0; font-size: 22px; }
281
+ .sv-fform-desc { margin: -10px 0 0; color: var(--sg-muted, #64748b); }
282
+ .sv-fform-field { display: flex; flex-direction: column; gap: 6px; }
283
+ .sv-fform-label { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; }
284
+ .sv-fform-req { color: var(--sg-danger, #dc2626); }
285
+ .sv-fform-field.is-missing :global(.sv-frv-box) { border-color: var(--sg-danger, #dc2626); }
286
+ .sv-fform-error { margin: 0; font-size: 12px; color: var(--sg-danger, #dc2626); }
287
+ .sv-fform-hint { margin: 0; font-size: 12px; color: var(--sg-muted, #64748b); }
288
+ .sv-fform-submit {
289
+ align-self: flex-start; border: 0; border-radius: 8px; padding: 8px 18px; font: inherit; font-weight: 600; cursor: pointer;
290
+ background: var(--sg-accent, #2563eb); color: var(--sg-accent-fg, #fff);
291
+ }
292
+ .sv-fform-submit:disabled { opacity: 0.6; cursor: progress; }
293
+ .sv-fform-failure { font-size: 13px; }
294
+ .sv-fform-done { align-items: flex-start; }
295
+ </style>
@@ -0,0 +1,22 @@
1
+ type $$ComponentProps = {
2
+ /** The grid's api. */
3
+ api: any;
4
+ /** The fields asked for, by column id, in order. Default: every field a person can fill in. */
5
+ fields?: ReadonlyArray<string>;
6
+ /** Fields that must be filled in before Submit. */
7
+ required?: ReadonlyArray<string>;
8
+ title?: string;
9
+ description?: string;
10
+ submitLabel?: string;
11
+ /** Values each record from this form starts with, under the row adder's `defaults` (`<SvBase>` gives an id). */
12
+ defaults?: () => Record<string, unknown>;
13
+ /**
14
+ * After the row was added. Return a Promise to wait on it: the button is
15
+ * busy until it settles, and a rejection takes the row back out of the
16
+ * grid, keeps what was typed and shows the error's message.
17
+ */
18
+ onSubmit?: (row: Record<string, unknown>) => void | Promise<unknown>;
19
+ };
20
+ declare const SvBaseForm: import("svelte").Component<$$ComponentProps, {}, "">;
21
+ type SvBaseForm = ReturnType<typeof SvBaseForm>;
22
+ export default SvBaseForm;