@svgrid/enterprise 3.1.22 → 3.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/SvGridBoard.svelte +39 -0
  2. package/dist/SvGridGallery.svelte +23 -0
  3. package/dist/SvGridScheduler.svelte +25 -7
  4. package/dist/cdn/svgrid-enterprise.svelte-external.js +13739 -13667
  5. package/dist/designer/assets/index-CHx3F4Ij.css +1 -0
  6. package/dist/designer/assets/{index-ICCQKTGR.js → index-CIxqRzKJ.js} +318 -318
  7. package/dist/designer/assets/merges-Bg-KqPEl.js +1 -0
  8. package/dist/designer/index.html +2 -2
  9. package/dist/fields/RecordView.svelte +5 -2
  10. package/dist/fields/activity.svelte.d.ts +5 -2
  11. package/dist/fields/activity.svelte.js +4 -3
  12. package/dist/fields/grid-access.d.ts +1 -1
  13. package/dist/fields/grid-access.js +13 -3
  14. package/dist/fields/index.d.ts +18 -0
  15. package/dist/fields/index.js +31 -0
  16. package/dist/node/studio.js +2 -2
  17. package/dist/review/GhostCell.svelte +60 -0
  18. package/dist/review/GhostCell.svelte.d.ts +14 -0
  19. package/dist/review/RichCellMark.svelte +72 -0
  20. package/dist/review/RichCellMark.svelte.d.ts +8 -0
  21. package/dist/review/SvReviewPanel.svelte +759 -0
  22. package/dist/review/SvReviewPanel.svelte.d.ts +16 -0
  23. package/dist/review/adapters.d.ts +79 -0
  24. package/dist/review/adapters.js +119 -0
  25. package/dist/review/agent.d.ts +52 -0
  26. package/dist/review/agent.js +232 -0
  27. package/dist/review/card-marks.svelte.d.ts +42 -0
  28. package/dist/review/card-marks.svelte.js +51 -0
  29. package/dist/review/cluster.d.ts +26 -0
  30. package/dist/review/cluster.js +377 -0
  31. package/dist/review/equal.d.ts +9 -0
  32. package/dist/review/equal.js +35 -0
  33. package/dist/review/formats.d.ts +31 -0
  34. package/dist/review/formats.js +37 -0
  35. package/dist/review/ghost.d.ts +17 -0
  36. package/dist/review/ghost.js +104 -0
  37. package/dist/review/index.d.ts +42 -0
  38. package/dist/review/index.js +36 -0
  39. package/dist/review/messages.d.ts +113 -0
  40. package/dist/review/messages.js +135 -0
  41. package/dist/review/model.svelte.d.ts +262 -0
  42. package/dist/review/model.svelte.js +883 -0
  43. package/dist/review/pending-filter.d.ts +26 -0
  44. package/dist/review/pending-filter.js +71 -0
  45. package/dist/review/rich-cells.d.ts +1 -0
  46. package/dist/review/rich-cells.js +36 -0
  47. package/dist/review/types.d.ts +83 -0
  48. package/dist/review/types.js +7 -0
  49. package/dist/upgrade-prompt.d.ts +1 -1
  50. package/dist/version.d.ts +2 -2
  51. package/dist/version.js +2 -2
  52. package/dist/wc/merges-DQTOlMWM.js +109 -0
  53. package/dist/wc/sv-sheet-element.js +5688 -5735
  54. package/package.json +9 -3
  55. package/dist/designer/assets/index-9APkRkTg.css +0 -1
@@ -0,0 +1 @@
1
+ import{n as e}from"./rolldown-runtime-Dd_uD5pT.js";var t=e({buildMergeIndex:()=>r,endOf:()=>s,expandRectToMerges:()=>c,isCovered:()=>a,mergeAt:()=>i,mergeDrawAt:()=>u,originOf:()=>o,stepPastMerge:()=>l}),n=(e,t)=>`${e}:${t}`;function r(e){if(!e||e.length===0)return null;let t=new Map,r=[];for(let i of e){let e=Math.max(1,Math.floor(i.rowSpan)),a=Math.max(1,Math.floor(i.colSpan));if(e===1&&a===1)continue;let o={rowIndex:i.rowIndex,colIndex:i.colIndex,rowSpan:e,colSpan:a};r.push(o);for(let r=o.rowIndex;r<o.rowIndex+e;r+=1)for(let e=o.colIndex;e<o.colIndex+a;e+=1)t.set(n(r,e),o)}return r.length?{list:r,byCell:t}:null}function i(e,t,r){return e?.byCell.get(n(t,r))}function a(e,t,n){let r=i(e,t,n);return!!r&&(r.rowIndex!==t||r.colIndex!==n)}function o(e,t,n){let r=i(e,t,n);return r?{rowIndex:r.rowIndex,colIndex:r.colIndex}:{rowIndex:t,colIndex:n}}function s(e,t,n){let r=i(e,t,n);return r?{rowIndex:r.rowIndex+r.rowSpan-1,colIndex:r.colIndex+r.colSpan-1}:{rowIndex:t,colIndex:n}}function c(e,t){if(!e)return t;let{minRow:n,maxRow:r,minCol:i,maxCol:a}=t;for(let t=0;t<64;t+=1){let t=!1;for(let o of e.list){let e=o.rowIndex+o.rowSpan-1,s=o.colIndex+o.colSpan-1;o.rowIndex<=r&&e>=n&&o.colIndex<=a&&s>=i&&(o.rowIndex<n&&(n=o.rowIndex,t=!0),e>r&&(r=e,t=!0),o.colIndex<i&&(i=o.colIndex,t=!0),s>a&&(a=s,t=!0))}if(!t)break}return n===t.minRow&&r===t.maxRow&&i===t.minCol&&a===t.maxCol?t:{minRow:n,maxRow:r,minCol:i,maxCol:a}}function l(e,t,n,r){if(!e)return n;let a=i(e,n.rowIndex,n.colIndex);if(!a)return n;if(i(e,t.rowIndex,t.colIndex)!==a)return{rowIndex:a.rowIndex,colIndex:a.colIndex};let o=Math.sign(n.rowIndex-t.rowIndex),s=Math.sign(n.colIndex-t.colIndex);if(o===0&&s===0)return{rowIndex:a.rowIndex,colIndex:a.colIndex};let c=n.rowIndex,l=n.colIndex;if(o>0?c=a.rowIndex+a.rowSpan:o<0&&(c=a.rowIndex-1),s>0?l=a.colIndex+a.colSpan:s<0&&(l=a.colIndex-1),c<0||c>r.maxRow||l<0||l>r.maxCol)return{rowIndex:a.rowIndex,colIndex:a.colIndex};let u=i(e,c,l);return u?{rowIndex:u.rowIndex,colIndex:u.colIndex}:{rowIndex:c,colIndex:l}}function u(e,t,n,r){let a=i(e,t,n);if(!a)return null;let o=Math.max(a.rowIndex,r.firstRow),s=Math.max(a.colIndex,r.firstCol);if(t!==o||n!==s)return{kind:`skip`};let c=o===a.rowIndex&&s===a.colIndex,l=Math.max(1,Math.min(a.rowIndex+a.rowSpan-t,r.lastRow-t+1)),u=Math.max(1,Math.min(a.colIndex+a.colSpan-n,r.lastCol-n+1));return{kind:c?`origin`:`continuation`,merge:a,rowSpan:l,colSpan:u}}export{t};
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>SvGrid Studio - Designer</title>
7
- <script type="module" crossorigin src="./assets/index-ICCQKTGR.js"></script>
7
+ <script type="module" crossorigin src="./assets/index-CIxqRzKJ.js"></script>
8
8
  <link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-Dd_uD5pT.js">
9
9
  <link rel="modulepreload" crossorigin href="./assets/disclose-version-CIm4S9xS.js">
10
10
  <link rel="modulepreload" crossorigin href="./assets/cell-formatting-JoxA0t65.js">
@@ -45,7 +45,7 @@
45
45
  <link rel="stylesheet" crossorigin href="./assets/SvMenuList-DJ47ba1h.css">
46
46
  <link rel="stylesheet" crossorigin href="./assets/SvGridChart-BgLuPO7I.css">
47
47
  <link rel="stylesheet" crossorigin href="./assets/SvGridChartPanel-4Pc8y5yO.css">
48
- <link rel="stylesheet" crossorigin href="./assets/index-9APkRkTg.css">
48
+ <link rel="stylesheet" crossorigin href="./assets/index-CHx3F4Ij.css">
49
49
  </head>
50
50
  <body>
51
51
  <div id="app"></div>
@@ -143,7 +143,7 @@
143
143
 
144
144
  // ---- Activity ---------------------------------------------------------------
145
145
  const allCols = $derived([...columns, ...hidden])
146
- type FeedItem = { key: string; at: number; by?: string; automation?: string; remote?: boolean; kind: 'edit' | 'added' | 'comment'; field: string; text: string; from?: string }
146
+ type FeedItem = { key: string; at: number; by?: string; automation?: string; via?: string; remote?: boolean; kind: 'edit' | 'added' | 'comment'; field: string; text: string; from?: string }
147
147
  const feed = $derived.by(() => {
148
148
  if (!row) return []
149
149
  const edits = changesOf(ctrl, row.id).map((ch): FeedItem => {
@@ -155,6 +155,7 @@
155
155
  at: ch.at,
156
156
  by: ch.by,
157
157
  automation: ch.automation,
158
+ via: ch.via,
158
159
  remote: ch.remote,
159
160
  kind: 'edit',
160
161
  field: c ? label(c) : ch.columnId,
@@ -166,8 +167,10 @@
166
167
  const notes = commentsOf(ctrl, row.id).map((n): FeedItem => ({ key: n.id, at: n.at, by: n.by, kind: 'comment', field: '', text: n.text }))
167
168
  return [...edits, ...notes].sort((a, b) => a.at - b.at)
168
169
  })
170
+ const person = (item: FeedItem) => (item.by ? (findFieldUser(item.by)?.name ?? 'Someone') : item.remote ? 'Someone else' : 'You')
171
+ // A proposal someone accepted (change review) is credited to what proposed it, and to them.
169
172
  const who = (item: FeedItem) =>
170
- item.automation ? `Automation: ${item.automation}` : item.by ? (findFieldUser(item.by)?.name ?? 'Someone') : item.remote ? 'Someone else' : 'You'
173
+ item.automation ? `Automation: ${item.automation}` : item.via ? `${item.via} (accepted by ${person(item)})` : person(item)
171
174
  // Shown here: a change made elsewhere is seen, so the grid stops marking it.
172
175
  $effect(() => {
173
176
  const id = row?.id
@@ -25,6 +25,8 @@ export type RecordChange = {
25
25
  by?: string;
26
26
  /** The automation that made it, by name. */
27
27
  automation?: string;
28
+ /** What proposed it, when `by` accepted a proposal (change review): "CRM agent". */
29
+ via?: string;
28
30
  /** Made elsewhere (another user, a script, an agent) and read from the database. */
29
31
  remote?: boolean;
30
32
  /** The record was added elsewhere: one entry, with no field. */
@@ -60,9 +62,10 @@ export declare function setRecordComments(next: RecordCommentStore | null): void
60
62
  /**
61
63
  * Note a change to a cell (called after every edit, paste, fill and clear).
62
64
  * `scope` is the grid, or the table (its base and id) for a change made
63
- * outside it (an automation). `automation` names the automation that made it.
65
+ * outside it (an automation). `automation` names the automation that made it;
66
+ * `via` names what proposed a change the signed-in user accepted.
64
67
  */
65
- export declare function noteChange(scope: ActivityScope, rowId: string, columnId: string, value: unknown, automation?: string): void;
68
+ export declare function noteChange(scope: ActivityScope, rowId: string, columnId: string, value: unknown, automation?: string, via?: string): void;
66
69
  export declare function changesOf(scope: ActivityScope, rowId: string): ReadonlyArray<RecordChange>;
67
70
  /**
68
71
  * Note a change made elsewhere and read from the database: an activity entry
@@ -50,13 +50,14 @@ export function setRecordComments(next) {
50
50
  /**
51
51
  * Note a change to a cell (called after every edit, paste, fill and clear).
52
52
  * `scope` is the grid, or the table (its base and id) for a change made
53
- * outside it (an automation). `automation` names the automation that made it.
53
+ * outside it (an automation). `automation` names the automation that made it;
54
+ * `via` names what proposed a change the signed-in user accepted.
54
55
  */
55
- export function noteChange(scope, rowId, columnId, value, automation) {
56
+ export function noteChange(scope, rowId, columnId, value, automation, via) {
56
57
  const byRow = rowsOf(changes, scope, true);
57
58
  const list = byRow.get(rowId) ?? [];
58
59
  // Kept short: the panel shows the latest.
59
- list.push({ at: Date.now(), ...(automation ? { automation } : { by: getCurrentUser()?.id }), columnId, value: Array.isArray(value) ? [...value] : value });
60
+ list.push({ at: Date.now(), ...(automation ? { automation } : { by: getCurrentUser()?.id }), ...(via ? { via } : {}), columnId, value: Array.isArray(value) ? [...value] : value });
60
61
  if (list.length > 50)
61
62
  list.splice(0, list.length - 50);
62
63
  byRow.set(rowId, list);
@@ -16,7 +16,7 @@ export type FieldColumn = {
16
16
  };
17
17
  /** Called on every draw of a grid with fields turned on. */
18
18
  export declare function rememberGrid(ctrl: any): void;
19
- /** The grid an api belongs to; undefined until that grid has drawn once. */
19
+ /** The grid an api belongs to (or a `$state` proxy of it); undefined until that grid has drawn once. */
20
20
  export declare function gridOf(api: unknown): any;
21
21
  export declare const columnIdOf: (def: Record<string, any>) => string;
22
22
  /** Every data column (declared ones with a field), in screen order, hidden ones last. */
@@ -7,15 +7,25 @@
7
7
  import { getAdvancedFilterEngine } from '@svgrid/grid';
8
8
  // The grid behind each api object (the grid keeps one api per grid).
9
9
  const grids = new WeakMap();
10
+ // The same, by one of the api's methods. An app that keeps the api in
11
+ // `$state` hands over a proxy of it, which is not the object above; Svelte
12
+ // does not proxy functions, so the proxy's methods are the api's own.
13
+ const byMethod = new WeakMap();
10
14
  /** Called on every draw of a grid with fields turned on. */
11
15
  export function rememberGrid(ctrl) {
12
16
  const api = ctrl?.buildApi?.();
13
- if (api && !grids.has(api))
17
+ if (api && !grids.has(api)) {
14
18
  grids.set(api, ctrl);
19
+ if (typeof api.getData === 'function')
20
+ byMethod.set(api.getData, ctrl);
21
+ }
15
22
  }
16
- /** The grid an api belongs to; undefined until that grid has drawn once. */
23
+ /** The grid an api belongs to (or a `$state` proxy of it); undefined until that grid has drawn once. */
17
24
  export function gridOf(api) {
18
- return api && typeof api === 'object' ? grids.get(api) : undefined;
25
+ if (!api || typeof api !== 'object')
26
+ return undefined;
27
+ const getData = api.getData;
28
+ return grids.get(api) ?? (typeof getData === 'function' ? byMethod.get(getData) : undefined);
19
29
  }
20
30
  export const columnIdOf = (def) => String(def.id ?? def.field ?? '');
21
31
  /** Every data column (declared ones with a field), in screen order, hidden ones last. */
@@ -25,6 +25,24 @@ export type { FieldView, FieldViewType, ColorSetting } from './view-model.js';
25
25
  export type { FieldPropertyDef, FieldEditorContext, FieldFillContext, FieldRowContext } from './registry.svelte.js';
26
26
  export { BUILTIN_FIELD_TYPES } from './builtin-types.js';
27
27
  export { setAttachmentUploader, attachmentsFromText, attachmentsText, toAttachments, type Attachment, type AttachmentFieldProps, type AttachmentUploader, type AttachmentUploadContext, } from './attachments.js';
28
+ /**
29
+ * Change review (`@svgrid/enterprise/review`) in SvGrid Base: what an edit
30
+ * does, done for each accepted proposal. Last modified time / by fields are
31
+ * stamped, and the record's activity notes the change, credited to the
32
+ * signed-in user who accepted it and to what proposed it ("CRM agent
33
+ * (accepted by Dana)"). An undo is noted as the user's own change.
34
+ *
35
+ * ```ts
36
+ * const review = createReview({ api: () => api, setRows, onApply: baseReviewApplied(() => api) })
37
+ * ```
38
+ */
39
+ export declare function baseReviewApplied(api: unknown): (applied: ReadonlyArray<{
40
+ rowId: string;
41
+ field: string;
42
+ source: {
43
+ label: string;
44
+ };
45
+ }>, how: 'accept' | 'undo' | 'redo', rows?: ReadonlyArray<unknown>) => void;
28
46
  /**
29
47
  * Turn field types on for every `<SvGrid>` on the page: registers the
30
48
  * built-in types and plugs the cell drawing into the grid. Safe to call more
@@ -184,6 +184,37 @@ function stampModified(ctrl, row, columnId) {
184
184
  overlay(id, (row[c.field] = author));
185
185
  }
186
186
  }
187
+ /**
188
+ * Change review (`@svgrid/enterprise/review`) in SvGrid Base: what an edit
189
+ * does, done for each accepted proposal. Last modified time / by fields are
190
+ * stamped, and the record's activity notes the change, credited to the
191
+ * signed-in user who accepted it and to what proposed it ("CRM agent
192
+ * (accepted by Dana)"). An undo is noted as the user's own change.
193
+ *
194
+ * ```ts
195
+ * const review = createReview({ api: () => api, setRows, onApply: baseReviewApplied(() => api) })
196
+ * ```
197
+ */
198
+ export function baseReviewApplied(api) {
199
+ return (applied, how, next) => {
200
+ const ctrl = gridOf(typeof api === 'function' ? api() : api);
201
+ if (!ctrl)
202
+ return;
203
+ const gridApi = ctrl.buildApi?.();
204
+ const idOf = gridApi?.getOption?.('getRowId');
205
+ const rows = new Map();
206
+ (next ?? gridApi?.getData?.() ?? []).forEach((r, i) => rows.set(String(idOf ? idOf(r, i) : i), r));
207
+ const columnOf = new Map((ctrl.allColumns ?? []).map((c) => [c.columnDef?.field, c.id]));
208
+ for (const p of applied) {
209
+ const row = rows.get(p.rowId);
210
+ const columnId = columnOf.get(p.field);
211
+ if (!row || !columnId)
212
+ continue;
213
+ stampModified(ctrl, row, columnId);
214
+ noteChange(ctrl, p.rowId, columnId, row[p.field], undefined, how === 'undo' ? undefined : p.source.label);
215
+ }
216
+ };
217
+ }
187
218
  /** The record's activity: what a cell now holds after an edit. */
188
219
  function noteEdit(ctrl, row, columnId) {
189
220
  if (!row)
@@ -16506,7 +16506,7 @@ function hasFieldConditions(schema) {
16506
16506
  *
16507
16507
  * `version.test.ts` asserts this matches `package.json`, so the two cannot drift.
16508
16508
  */
16509
- var SVGRID_VERSION = "3.1.22";
16509
+ var SVGRID_VERSION = "3.1.23";
16510
16510
  /**
16511
16511
  * The version of `@svgrid/grid`, which releases on its own line: enterprise is
16512
16512
  * on 3.1.x while the grid is on 3.0.x.
@@ -16519,7 +16519,7 @@ var SVGRID_VERSION = "3.1.22";
16519
16519
  *
16520
16520
  * `version.test.ts` asserts this matches packages/grid/package.json.
16521
16521
  */
16522
- var GRID_VERSION = "3.0.21";
16522
+ var GRID_VERSION = "3.0.22";
16523
16523
  //#endregion
16524
16524
  //#region src/studio/emit-project.ts
16525
16525
  var has = (blocks, kind) => blocks.some((b) => b.config.kind === kind);
@@ -0,0 +1,60 @@
1
+ <!--
2
+ A cell with a pending proposal: the proposed value, then what the cell
3
+ holds now, struck through. `own` is what the column's own `cell` returned (a
4
+ component, a snippet, text, or nothing for a plain column).
5
+ -->
6
+ <script lang="ts">
7
+ import type { Component, Snippet } from 'svelte'
8
+
9
+ let { own, before, beforeText, after, empty, title }: {
10
+ own: unknown
11
+ before: unknown
12
+ beforeText: string
13
+ after: string
14
+ /** The text for a blank proposed value, in the review's wording. */
15
+ empty: string
16
+ /** "{source} proposes {value} - reason", built by the wrapper. */
17
+ title: string
18
+ } = $props()
19
+
20
+ // The grid's render configs (`renderComponent` / `renderSnippet`) are read by
21
+ // shape: the classes are not part of the public surface.
22
+ type ComponentConfig = { component: Component<any>; props?: Record<string, unknown> }
23
+ type SnippetConfig = { snippet: Snippet<[unknown]>; params?: unknown }
24
+ const isObj = (v: unknown): v is Record<string, unknown> => v !== null && typeof v === 'object'
25
+ const ownComponent = $derived(isObj(own) && typeof own.component === 'function' ? (own as ComponentConfig) : null)
26
+ const ownSnippet = $derived(isObj(own) && typeof own.snippet === 'function' ? (own as SnippetConfig) : null)
27
+ const ownText = $derived(typeof own === 'string' || typeof own === 'number' ? String(own) : beforeText)
28
+ </script>
29
+
30
+ <span class="sv-review-ghost" {title}>
31
+ <span class="sv-review-ghost-after">{after || empty}</span>
32
+ {#if before != null && before !== ''}
33
+ <s class="sv-review-ghost-before">
34
+ {#if ownComponent}<ownComponent.component {...ownComponent.props ?? {}} />{:else if ownSnippet}{@render ownSnippet.snippet(ownSnippet.params)}{:else}{ownText}{/if}
35
+ </s>
36
+ {/if}
37
+ </span>
38
+
39
+ <style>
40
+ /* Inline text beside the grid's own icon: a block or flex box here would drop
41
+ to a line of its own. The proposed value comes first and is plain text, so
42
+ the cell's ellipsis cuts its end. The struck value is one box, at most half
43
+ the cell: when it does not fit, the ellipsis takes it whole instead of
44
+ leaving a cut-off fragment, and the cell's title still names it. */
45
+ .sv-review-ghost-before {
46
+ display: inline-block;
47
+ max-width: 50%;
48
+ margin-left: 6px;
49
+ overflow: hidden;
50
+ text-overflow: ellipsis;
51
+ white-space: nowrap;
52
+ vertical-align: bottom;
53
+ color: var(--sg-muted, #6b7280);
54
+ text-decoration-thickness: 1px;
55
+ }
56
+ .sv-review-ghost-after {
57
+ color: var(--sg-fg, inherit);
58
+ font-weight: 600;
59
+ }
60
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { Component } from 'svelte';
2
+ type $$ComponentProps = {
3
+ own: unknown;
4
+ before: unknown;
5
+ beforeText: string;
6
+ after: string;
7
+ /** The text for a blank proposed value, in the review's wording. */
8
+ empty: string;
9
+ /** "{source} proposes {value} - reason", built by the wrapper. */
10
+ title: string;
11
+ };
12
+ declare const GhostCell: Component<$$ComponentProps, {}, "">;
13
+ type GhostCell = ReturnType<typeof GhostCell>;
14
+ export default GhostCell;
@@ -0,0 +1,72 @@
1
+ <!--
2
+ The proposed value after a rich cell (a select, a rating, a person): the
3
+ cell draws as it always does, and "-> Closed" follows it. The text is the
4
+ column's own formatter's (a person's name, not their id), else the plain
5
+ formatted value.
6
+ -->
7
+ <script lang="ts">
8
+ import { formatValueForExport } from '@svgrid/grid'
9
+ import { cellProposalFor } from './card-marks.svelte'
10
+ import { showValue } from './cluster'
11
+ import { fill } from './messages'
12
+
13
+ let { ctrl, row, column }: { ctrl: any; row: any; column: any } = $props()
14
+
15
+ /** A value's label from a static options list ({ value, label } or plain values); null when there is none. */
16
+ function optionLabels(options: unknown): ((v: unknown) => string | null) | null {
17
+ if (!Array.isArray(options)) return null
18
+ const byValue = new Map<string, string>()
19
+ for (const o of options) {
20
+ if (o && typeof o === 'object' && 'value' in o) byValue.set(String((o as { value: unknown }).value), String((o as { label?: unknown }).label ?? (o as { value: unknown }).value))
21
+ else byValue.set(String(o), String(o))
22
+ }
23
+ return (v) => byValue.get(String(v)) ?? null
24
+ }
25
+
26
+ const proposal = $derived(cellProposalFor(ctrl?.props?.getRowId, row?.original, String(row?.id), column?.columnDef?.field))
27
+ const text = $derived.by(() => {
28
+ if (!proposal) return ''
29
+ const def = column?.columnDef ?? {}
30
+ const value = proposal.after
31
+ if (value == null || value === '') return proposal.empty
32
+ if (typeof def.formatter === 'function') {
33
+ try {
34
+ return String(def.formatter({ value, row, column, table: ctrl?.grid }) ?? '')
35
+ } catch {
36
+ // A formatter that needs more than a value: fall back to the value.
37
+ }
38
+ }
39
+ // A list or chips column shows option labels, not stored values.
40
+ const labelOf = optionLabels(def.editorOptions)
41
+ const one = (v: unknown) => labelOf?.(v) ?? (Array.isArray(value) ? showValue(v).replace(/^"|"$/g, '') : formatValueForExport(v, def.format))
42
+ return Array.isArray(value) ? value.map(one).join(', ') : one(value)
43
+ })
44
+ </script>
45
+
46
+ {#if proposal}
47
+ <span
48
+ class="sv-review-cellmark"
49
+ title="{fill(proposal.proposes, { source: proposal.source, value: text })}{proposal.reason ? ` - ${proposal.reason}` : ''}"
50
+ ><span aria-hidden="true">→</span> {text}</span>
51
+ {/if}
52
+
53
+ <style>
54
+ .sv-review-cellmark {
55
+ display: inline-flex;
56
+ align-items: center;
57
+ gap: 3px;
58
+ margin-left: 6px;
59
+ flex: none;
60
+ max-width: 60%;
61
+ overflow: hidden;
62
+ white-space: nowrap;
63
+ text-overflow: ellipsis;
64
+ font-size: 0.92em;
65
+ font-weight: 600;
66
+ color: var(--sg-fg, #111827);
67
+ }
68
+ .sv-review-cellmark > span {
69
+ color: var(--sg-muted, #6b7280);
70
+ font-weight: 400;
71
+ }
72
+ </style>
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ ctrl: any;
3
+ row: any;
4
+ column: any;
5
+ };
6
+ declare const RichCellMark: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type RichCellMark = ReturnType<typeof RichCellMark>;
8
+ export default RichCellMark;