@svgrid/enterprise 3.1.21 → 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.
- package/dist/SvGridBoard.svelte +39 -0
- package/dist/SvGridGallery.svelte +23 -0
- package/dist/SvGridRowCount.svelte +23 -0
- package/dist/SvGridRowCount.svelte.d.ts +6 -0
- package/dist/SvGridScheduler.svelte +25 -7
- package/dist/cdn/svgrid-enterprise.svelte-external.js +13838 -13554
- package/dist/designer/assets/GridFooter-DKMfZpL4.js +2 -0
- package/dist/designer/assets/GridMenus-DWDghydL.js +6 -0
- package/dist/designer/assets/{PinSplitter-C5AwA1xV.js → PinSplitter-BfIjd7pi.js} +1 -1
- package/dist/designer/assets/grid-extension.svelte-G1ng0xd2.js +1 -0
- package/dist/designer/assets/index-CHx3F4Ij.css +1 -0
- package/dist/designer/assets/{index-DLJjuVA4.js → index-CIxqRzKJ.js} +324 -324
- package/dist/designer/assets/merges-Bg-KqPEl.js +1 -0
- package/dist/designer/index.html +3 -3
- package/dist/expressions/compile.js +11 -7
- package/dist/expressions/evaluate.js +4 -0
- package/dist/expressions/expression-columns.d.ts +4 -1
- package/dist/expressions/expression-columns.js +12 -3
- package/dist/expressions/parse.js +19 -7
- package/dist/fields/RecordView.svelte +5 -2
- package/dist/fields/activity.svelte.d.ts +5 -2
- package/dist/fields/activity.svelte.js +4 -3
- package/dist/fields/grid-access.d.ts +1 -1
- package/dist/fields/grid-access.js +13 -3
- package/dist/fields/index.d.ts +18 -0
- package/dist/fields/index.js +31 -0
- package/dist/filter-labels.d.ts +16 -0
- package/dist/filter-labels.js +83 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +7 -0
- package/dist/install.js +8 -0
- package/dist/node/studio.js +12 -5
- package/dist/relative-date-core.d.ts +40 -0
- package/dist/relative-date-core.js +85 -0
- package/dist/relative-dates.d.ts +28 -0
- package/dist/relative-dates.js +61 -0
- package/dist/review/GhostCell.svelte +60 -0
- package/dist/review/GhostCell.svelte.d.ts +14 -0
- package/dist/review/RichCellMark.svelte +72 -0
- package/dist/review/RichCellMark.svelte.d.ts +8 -0
- package/dist/review/SvReviewPanel.svelte +759 -0
- package/dist/review/SvReviewPanel.svelte.d.ts +16 -0
- package/dist/review/adapters.d.ts +79 -0
- package/dist/review/adapters.js +119 -0
- package/dist/review/agent.d.ts +52 -0
- package/dist/review/agent.js +232 -0
- package/dist/review/card-marks.svelte.d.ts +42 -0
- package/dist/review/card-marks.svelte.js +51 -0
- package/dist/review/cluster.d.ts +26 -0
- package/dist/review/cluster.js +377 -0
- package/dist/review/equal.d.ts +9 -0
- package/dist/review/equal.js +35 -0
- package/dist/review/formats.d.ts +31 -0
- package/dist/review/formats.js +37 -0
- package/dist/review/ghost.d.ts +17 -0
- package/dist/review/ghost.js +104 -0
- package/dist/review/index.d.ts +42 -0
- package/dist/review/index.js +36 -0
- package/dist/review/messages.d.ts +113 -0
- package/dist/review/messages.js +135 -0
- package/dist/review/model.svelte.d.ts +262 -0
- package/dist/review/model.svelte.js +883 -0
- package/dist/review/pending-filter.d.ts +26 -0
- package/dist/review/pending-filter.js +71 -0
- package/dist/review/rich-cells.d.ts +1 -0
- package/dist/review/rich-cells.js +36 -0
- package/dist/review/types.d.ts +83 -0
- package/dist/review/types.js +7 -0
- package/dist/row-count.d.ts +28 -0
- package/dist/row-count.js +39 -0
- package/dist/sort-blanks.d.ts +8 -0
- package/dist/sort-blanks.js +47 -0
- package/dist/studio/ui-components.generated.js +1 -1
- package/dist/upgrade-prompt.d.ts +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/wc/GridFooter-CKC4L36O.js +162 -0
- package/dist/wc/{GridMenus-DVGuqp2l.js → GridMenus-h7RsvcmI.js} +320 -303
- package/dist/wc/{PinSplitter-Btco0txr.js → PinSplitter-BN6ybmjs.js} +1 -1
- package/dist/wc/grid-extension.svelte-CDf5EUyz.js +27 -0
- package/dist/wc/merges-DQTOlMWM.js +109 -0
- package/dist/wc/sv-sheet-element.js +6753 -6792
- package/package.json +9 -3
- package/dist/designer/assets/GridFooter-CKBVItAU.js +0 -2
- package/dist/designer/assets/GridMenus-Dzwu7M9r.js +0 -6
- package/dist/designer/assets/grid-extension.svelte-BZ7AqyJI.js +0 -1
- package/dist/designer/assets/index-WCxj6WfJ.css +0 -1
- package/dist/wc/GridFooter-BgoA77TJ.js +0 -145
- package/dist/wc/grid-extension.svelte-CucBS4FL.js +0 -20
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** The relative date operator ids, in menu order. */
|
|
2
|
+
export const RELATIVE_DATE_OPERATOR_IDS = ['today', 'beforeToday', 'afterToday', 'next7Days', 'last7Days'];
|
|
3
|
+
const DAY_MS = 86400000;
|
|
4
|
+
/** Whether `op` is one of the relative date operators. */
|
|
5
|
+
export function isRelativeDateOperator(op) {
|
|
6
|
+
return RELATIVE_DATE_OPERATOR_IDS.includes(op);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A value as a count of local calendar days, or NaN. A bare 'YYYY-MM-DD' is
|
|
10
|
+
* that day in local time; `new Date()` would read it as UTC midnight, the day
|
|
11
|
+
* before anywhere west of Greenwich.
|
|
12
|
+
*/
|
|
13
|
+
function localDayNumber(value) {
|
|
14
|
+
let d;
|
|
15
|
+
if (value instanceof Date)
|
|
16
|
+
d = value;
|
|
17
|
+
else if (typeof value === 'number')
|
|
18
|
+
d = new Date(value);
|
|
19
|
+
else if (typeof value === 'string' && value.trim()) {
|
|
20
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim());
|
|
21
|
+
d = m ? new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3])) : new Date(value);
|
|
22
|
+
}
|
|
23
|
+
else
|
|
24
|
+
return NaN;
|
|
25
|
+
if (Number.isNaN(d.getTime()))
|
|
26
|
+
return NaN;
|
|
27
|
+
return Math.round(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()) / DAY_MS);
|
|
28
|
+
}
|
|
29
|
+
/** The inclusive range of local day numbers an operator keeps. */
|
|
30
|
+
function dayRange(op, now) {
|
|
31
|
+
const t = localDayNumber(now);
|
|
32
|
+
switch (op) {
|
|
33
|
+
case 'today':
|
|
34
|
+
return [t, t];
|
|
35
|
+
case 'beforeToday':
|
|
36
|
+
return [-Infinity, t - 1];
|
|
37
|
+
case 'afterToday':
|
|
38
|
+
return [t + 1, Infinity];
|
|
39
|
+
case 'next7Days':
|
|
40
|
+
return [t, t + 7];
|
|
41
|
+
case 'last7Days':
|
|
42
|
+
return [t - 7, t];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Local 'YYYY-MM-DD' for a day number from `localDayNumber`. */
|
|
46
|
+
function isoFromDayNumber(day) {
|
|
47
|
+
return new Date(day * DAY_MS).toISOString().slice(0, 10);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The test for one relative date operator. "Today" is read once, here, so a
|
|
51
|
+
* page left open past midnight keeps the old day until the filter changes.
|
|
52
|
+
* A blank or unparseable cell never matches.
|
|
53
|
+
*/
|
|
54
|
+
export function compileRelativeDateFilter(op, now = new Date()) {
|
|
55
|
+
const [lo, hi] = dayRange(op, now);
|
|
56
|
+
return (cellValue) => {
|
|
57
|
+
const day = localDayNumber(cellValue);
|
|
58
|
+
return day >= lo && day <= hi;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A relative date filter as the absolute one it means today, for a backend
|
|
63
|
+
* with no "today" of its own: `beforeToday` becomes `lessThan` today's date,
|
|
64
|
+
* `next7Days` becomes `between` today and a week out, and so on, with dates as
|
|
65
|
+
* local 'YYYY-MM-DD'. Any other filter is returned as it is.
|
|
66
|
+
*
|
|
67
|
+
* A server data source gets this applied for it. Under `externalFilter` the
|
|
68
|
+
* `onFiltersChange` payload keeps the relative operator, so a saved view still
|
|
69
|
+
* means "overdue" tomorrow; call this when you build the query.
|
|
70
|
+
*/
|
|
71
|
+
export function resolveRelativeDateFilter(filter, now = new Date()) {
|
|
72
|
+
if (!isRelativeDateOperator(filter.operator))
|
|
73
|
+
return filter;
|
|
74
|
+
const op = filter.operator;
|
|
75
|
+
const [lo, hi] = dayRange(op, now);
|
|
76
|
+
const out = { ...filter };
|
|
77
|
+
delete out.valueTo;
|
|
78
|
+
if (op === 'beforeToday')
|
|
79
|
+
Object.assign(out, { operator: 'lessThan', value: isoFromDayNumber(hi + 1) });
|
|
80
|
+
else if (op === 'afterToday')
|
|
81
|
+
Object.assign(out, { operator: 'greaterThan', value: isoFromDayNumber(lo - 1) });
|
|
82
|
+
else
|
|
83
|
+
Object.assign(out, { operator: 'between', value: isoFromDayNumber(lo), valueTo: isoFromDayNumber(hi) });
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relative date filters - five date operators measured from today, in whole
|
|
3
|
+
* local days: Today, Before today (overdue), After today, Next 7 days and
|
|
4
|
+
* Last 7 days. They take no value, so picking one in a date column's filter
|
|
5
|
+
* menu filters at once. They plug into the grid's `filterOperators` seam, so a
|
|
6
|
+
* grid without this module carries none of the code.
|
|
7
|
+
*
|
|
8
|
+
* Like the rest of Enterprise it runs without a license key during the
|
|
9
|
+
* evaluation; the grid shows the watermark and a one-time console notice.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { enableRelativeDateFilters } from '@svgrid/enterprise'
|
|
13
|
+
* enableRelativeDateFilters()
|
|
14
|
+
* // then: a column with cellDataType 'date' or 'dateString' offers them
|
|
15
|
+
* api.setFilter('due', { operator: 'beforeToday' })
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { type GridFilterOperator } from '@svgrid/grid';
|
|
19
|
+
import { type RelativeDateOperator } from './relative-date-core.js';
|
|
20
|
+
export { RELATIVE_DATE_OPERATOR_IDS, isRelativeDateOperator, compileRelativeDateFilter, resolveRelativeDateFilter, type RelativeDateOperator, } from './relative-date-core.js';
|
|
21
|
+
/** The operators as the grid's `filterOperators` seam takes them. */
|
|
22
|
+
export declare const relativeDateOperators: Record<RelativeDateOperator, GridFilterOperator>;
|
|
23
|
+
/**
|
|
24
|
+
* Add the relative date operators to every grid's date filters. Idempotent:
|
|
25
|
+
* call it once at app start or from each component that uses them. Also
|
|
26
|
+
* invoked by {@link installEnterprise}.
|
|
27
|
+
*/
|
|
28
|
+
export declare function enableRelativeDateFilters(): void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relative date filters - five date operators measured from today, in whole
|
|
3
|
+
* local days: Today, Before today (overdue), After today, Next 7 days and
|
|
4
|
+
* Last 7 days. They take no value, so picking one in a date column's filter
|
|
5
|
+
* menu filters at once. They plug into the grid's `filterOperators` seam, so a
|
|
6
|
+
* grid without this module carries none of the code.
|
|
7
|
+
*
|
|
8
|
+
* Like the rest of Enterprise it runs without a license key during the
|
|
9
|
+
* evaluation; the grid shows the watermark and a one-time console notice.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { enableRelativeDateFilters } from '@svgrid/enterprise'
|
|
13
|
+
* enableRelativeDateFilters()
|
|
14
|
+
* // then: a column with cellDataType 'date' or 'dateString' offers them
|
|
15
|
+
* api.setFilter('due', { operator: 'beforeToday' })
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { getGridExtension, registerGridExtension } from '@svgrid/grid';
|
|
19
|
+
import { hasValidLicense } from './license.js';
|
|
20
|
+
import { emitUnlicensedNudge } from './watermark.js';
|
|
21
|
+
import { compileRelativeDateFilter, resolveRelativeDateFilter, } from './relative-date-core.js';
|
|
22
|
+
export { RELATIVE_DATE_OPERATOR_IDS, isRelativeDateOperator, compileRelativeDateFilter, resolveRelativeDateFilter, } from './relative-date-core.js';
|
|
23
|
+
const LABELS = {
|
|
24
|
+
today: 'Today',
|
|
25
|
+
beforeToday: 'Before today',
|
|
26
|
+
afterToday: 'After today',
|
|
27
|
+
next7Days: 'Next 7 days',
|
|
28
|
+
last7Days: 'Last 7 days',
|
|
29
|
+
};
|
|
30
|
+
function operator(op) {
|
|
31
|
+
return {
|
|
32
|
+
label: LABELS[op],
|
|
33
|
+
types: ['date'],
|
|
34
|
+
valueless: true,
|
|
35
|
+
compile: () => compileRelativeDateFilter(op),
|
|
36
|
+
toServer: (filter) => resolveRelativeDateFilter(filter),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** The operators as the grid's `filterOperators` seam takes them. */
|
|
40
|
+
export const relativeDateOperators = {
|
|
41
|
+
today: operator('today'),
|
|
42
|
+
beforeToday: operator('beforeToday'),
|
|
43
|
+
afterToday: operator('afterToday'),
|
|
44
|
+
next7Days: operator('next7Days'),
|
|
45
|
+
last7Days: operator('last7Days'),
|
|
46
|
+
};
|
|
47
|
+
let enabled = false;
|
|
48
|
+
/**
|
|
49
|
+
* Add the relative date operators to every grid's date filters. Idempotent:
|
|
50
|
+
* call it once at app start or from each component that uses them. Also
|
|
51
|
+
* invoked by {@link installEnterprise}.
|
|
52
|
+
*/
|
|
53
|
+
export function enableRelativeDateFilters() {
|
|
54
|
+
if (enabled)
|
|
55
|
+
return;
|
|
56
|
+
enabled = true;
|
|
57
|
+
// Another module may have added operators of its own; keep them.
|
|
58
|
+
registerGridExtension({ filterOperators: { ...getGridExtension()?.filterOperators, ...relativeDateOperators } });
|
|
59
|
+
if (!hasValidLicense())
|
|
60
|
+
emitUnlicensedNudge();
|
|
61
|
+
}
|
|
@@ -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>
|