@pstdio/ui 0.41.0 → 0.42.0

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 (85) hide show
  1. package/dist/{MermaidComponent-BCiI63_G.js → MermaidComponent-D7vhkpka.js} +2 -2
  2. package/dist/{MermaidComponent-BCiI63_G.js.map → MermaidComponent-D7vhkpka.js.map} +1 -1
  3. package/dist/alert-MQ47wy7f.js +388 -0
  4. package/dist/alert-MQ47wy7f.js.map +1 -0
  5. package/dist/chat-ui.d.ts +36 -5
  6. package/dist/chat-ui.js +1777 -1422
  7. package/dist/chat-ui.js.map +1 -1
  8. package/dist/collection-view-fields-D3cysHNT.d.ts +232 -0
  9. package/dist/collection-view-grouping-D4nZk8-3.js +90 -0
  10. package/dist/collection-view-grouping-D4nZk8-3.js.map +1 -0
  11. package/dist/collection-view-search-CpjJy76w.js +1973 -0
  12. package/dist/collection-view-search-CpjJy76w.js.map +1 -0
  13. package/dist/collection-view.d.ts +4 -0
  14. package/dist/collection-view.js +2 -0
  15. package/dist/{data-table-stats-row-EiTEONdY.js → data-table-stats-row-64magT2g.js} +2 -2
  16. package/dist/{data-table-stats-row-EiTEONdY.js.map → data-table-stats-row-64magT2g.js.map} +1 -1
  17. package/dist/data-table.d.ts +3 -1
  18. package/dist/data-table.js +279 -277
  19. package/dist/data-table.js.map +1 -1
  20. package/dist/{diff-drawer-BwZBOObe.js → diff-drawer-IYpQ-QQK.js} +192 -191
  21. package/dist/diff-drawer-IYpQ-QQK.js.map +1 -0
  22. package/dist/{diff-viewer-kOOd7C7h.js → diff-viewer-C-0VAt9P.js} +4 -4
  23. package/dist/{diff-viewer-kOOd7C7h.js.map → diff-viewer-C-0VAt9P.js.map} +1 -1
  24. package/dist/diff.js +2 -2
  25. package/dist/{display-sort-control-DPxc0e5z.js → display-sort-control-f3rbILsV.js} +87 -86
  26. package/dist/{display-sort-control-DPxc0e5z.js.map → display-sort-control-f3rbILsV.js.map} +1 -1
  27. package/dist/drop-indicator-HjlBSIeV.js +27 -0
  28. package/dist/drop-indicator-HjlBSIeV.js.map +1 -0
  29. package/dist/edit-mode-data-table-DnMB2jiy.js +1449 -0
  30. package/dist/edit-mode-data-table-DnMB2jiy.js.map +1 -0
  31. package/dist/host-storage-BIo5azql.js +31 -0
  32. package/dist/host-storage-BIo5azql.js.map +1 -0
  33. package/dist/host-storage-J0089Hyk.d.ts +17 -0
  34. package/dist/icon-options-Tt6Wzhtv.js +308 -0
  35. package/dist/icon-options-Tt6Wzhtv.js.map +1 -0
  36. package/dist/{index-C9mywhyX.d.ts → index-B79hwNcB.d.ts} +17 -1
  37. package/dist/index-CKsd23jv.d.ts +80 -0
  38. package/dist/{index-D3UJyWWh.d.ts → index-ICoad2I_.d.ts} +4 -223
  39. package/dist/index.d.ts +22 -6
  40. package/dist/index.js +1174 -1112
  41. package/dist/index.js.map +1 -1
  42. package/dist/kanban-renderer.d.ts +2 -1
  43. package/dist/kanban-renderer.js +531 -528
  44. package/dist/kanban-renderer.js.map +1 -1
  45. package/dist/list-row-Z4SHkseA.js +1195 -0
  46. package/dist/list-row-Z4SHkseA.js.map +1 -0
  47. package/dist/markdown-editor-Dd4mor7l.js +3236 -0
  48. package/dist/markdown-editor-Dd4mor7l.js.map +1 -0
  49. package/dist/number-input-7K66wnSC.js +63 -0
  50. package/dist/number-input-7K66wnSC.js.map +1 -0
  51. package/dist/param-editor-Dnix-fJo.js +225 -0
  52. package/dist/param-editor-Dnix-fJo.js.map +1 -0
  53. package/dist/param-editor-field-B3vLST-B.js +2394 -0
  54. package/dist/param-editor-field-B3vLST-B.js.map +1 -0
  55. package/dist/param-editor.js +4 -2
  56. package/dist/prism-OG4H7Hu4.js +334 -0
  57. package/dist/prism-OG4H7Hu4.js.map +1 -0
  58. package/dist/queued-follow-up-Bj_vtMgd.js +208 -0
  59. package/dist/queued-follow-up-Bj_vtMgd.js.map +1 -0
  60. package/dist/rich-text-BgEnAHr2.js +1160 -0
  61. package/dist/rich-text-BgEnAHr2.js.map +1 -0
  62. package/dist/rich-text.d.ts +1 -1
  63. package/dist/rich-text.js +4 -2
  64. package/dist/terminal.js +1 -1
  65. package/dist/{theme-at0rtSnX.js → theme-CMJdH59Z.js} +224 -90
  66. package/dist/theme-CMJdH59Z.js.map +1 -0
  67. package/dist/theme.js +1 -1
  68. package/dist/tree-list-jarhD31R.js +1652 -0
  69. package/dist/tree-list-jarhD31R.js.map +1 -0
  70. package/dist/use-virtualizer-uj43XjBp.js +10 -0
  71. package/dist/use-virtualizer-uj43XjBp.js.map +1 -0
  72. package/dist/{host-storage-DP0RLj4i.d.ts → workspace-badge-BKtB805G.d.ts} +6 -16
  73. package/package.json +6 -2
  74. package/dist/chat-question-bubble-w2vMbFsS.js +0 -68
  75. package/dist/chat-question-bubble-w2vMbFsS.js.map +0 -1
  76. package/dist/diff-drawer-BwZBOObe.js.map +0 -1
  77. package/dist/list-row-DM0DiIrN.js +0 -1167
  78. package/dist/list-row-DM0DiIrN.js.map +0 -1
  79. package/dist/prism-JVIsQMoJ.js +0 -1029
  80. package/dist/prism-JVIsQMoJ.js.map +0 -1
  81. package/dist/rich-text-DM24VNQZ.js +0 -10447
  82. package/dist/rich-text-DM24VNQZ.js.map +0 -1
  83. package/dist/theme-at0rtSnX.js.map +0 -1
  84. package/dist/tree-list-Cqgl8mnN.js +0 -1621
  85. package/dist/tree-list-Cqgl8mnN.js.map +0 -1
@@ -0,0 +1,232 @@
1
+ import "./host-storage-J0089Hyk.js";
2
+ import { ReactNode } from "react";
3
+ import { ViewFilterCondition, ViewFilterGroup, ViewFilterGroup as ViewFilterGroup$1, ViewFilterRule, ViewFilterRule as ViewFilterRule$1, ViewSort, ViewSort as ViewSort$1, ViewSortDirection } from "@pstdio/sdk/extensions";
4
+ //#region src/components/collection-view/collection-view-types.d.ts
5
+ declare const EMPTY_VIEW_FILTER: ViewFilterGroup;
6
+ /** What a saved view stores. Search, selection, and collapsed groups are screen state and never saved. */
7
+ interface CollectionSavedView<TSettings> {
8
+ id: string;
9
+ title: string;
10
+ settings: TSettings;
11
+ filter: ViewFilterGroup;
12
+ sorts: ViewSort[];
13
+ /** @deprecated Use defaultActiveViewId instead. */
14
+ isDefault?: boolean;
15
+ }
16
+ interface CollectionViewState<TSettings> {
17
+ settings: TSettings;
18
+ filter: ViewFilterGroup;
19
+ sorts: ViewSort[];
20
+ }
21
+ interface CollectionViewsSource<TSettings> {
22
+ views: (CollectionSavedView<TSettings> & {
23
+ builtIn: boolean;
24
+ })[];
25
+ defaultViewId: string;
26
+ onCreateView: (input: CollectionViewState<TSettings> & {
27
+ title: string;
28
+ copyFrom?: string;
29
+ }) => Promise<CollectionSavedView<TSettings>>;
30
+ onUpdateView: (id: string, input: Partial<CollectionViewState<TSettings>> & {
31
+ title?: string;
32
+ }) => Promise<void>;
33
+ onDeleteView: (id: string) => Promise<void>;
34
+ onSetDefaultView: (id: string | null) => Promise<void>;
35
+ }
36
+ //#endregion
37
+ //#region src/components/kanban-renderer/types.d.ts
38
+ type ViewMode = "board" | "list";
39
+ declare const NO_GROUPING = "none";
40
+ interface EnumOption {
41
+ value: string;
42
+ label: string;
43
+ color?: string;
44
+ icon?: string | null;
45
+ }
46
+ /**
47
+ * Reactive options source for enum / enum-multi attributes. Mirrors React's
48
+ * useSyncExternalStore contract so a contribution can hand its options off as
49
+ * a live collection (e.g. a project-scoped resource) instead of a frozen
50
+ * array, and the renderer subscribes to it the same way it subscribes to rows.
51
+ */
52
+ interface EnumOptionsSource {
53
+ subscribe: (listener: () => void) => () => void;
54
+ getSnapshot: () => EnumOption[];
55
+ }
56
+ type EnumOptions = EnumOption[] | EnumOptionsSource;
57
+ type AttributeType = {
58
+ kind: "enum";
59
+ options: EnumOptions;
60
+ } | {
61
+ kind: "enum-multi";
62
+ options: EnumOptions;
63
+ } | {
64
+ kind: "boolean";
65
+ legacyValues?: Record<string, boolean>;
66
+ } | {
67
+ kind: "string";
68
+ } | {
69
+ kind: "date";
70
+ } | {
71
+ kind: "number";
72
+ } | {
73
+ kind: "user";
74
+ };
75
+ declare const isEnumOptionsSource: (options: EnumOptions) => options is EnumOptionsSource;
76
+ type AttributeKind = AttributeType["kind"];
77
+ type AttributeDisplayDescriptor = {
78
+ kind: "badge-list";
79
+ itemsAttributeId: string;
80
+ };
81
+ type CollectionBadgeJsonValue = string | number | boolean | null | CollectionBadgeJsonValue[] | CollectionBadgeJsonObject;
82
+ interface CollectionBadgeJsonObject {
83
+ [key: string]: CollectionBadgeJsonValue;
84
+ }
85
+ interface CollectionBadgeResource {
86
+ type: string;
87
+ id: string;
88
+ projectId?: string;
89
+ label?: string;
90
+ extensionId?: string;
91
+ metadata?: CollectionBadgeJsonObject;
92
+ }
93
+ interface CollectionBadgeItem {
94
+ id: string;
95
+ label: string;
96
+ icon?: string;
97
+ resource?: CollectionBadgeResource;
98
+ }
99
+ interface AttributeDescriptor {
100
+ id: string;
101
+ label: string;
102
+ type: AttributeType;
103
+ filterable?: boolean;
104
+ groupable?: boolean;
105
+ sortable?: boolean;
106
+ displayable?: boolean;
107
+ editable?: boolean;
108
+ display?: AttributeDisplayDescriptor;
109
+ render?: (value: unknown, row: KanbanRendererRow) => ReactNode;
110
+ compare?: (a: unknown, b: unknown) => number;
111
+ }
112
+ /**
113
+ * Reactive source for an entire attribute schema. Mirrors React's
114
+ * useSyncExternalStore contract so consumers (e.g. the workbench's
115
+ * kanban-renderer bridge) can subscribe and re-resolve when the schema changes
116
+ * — letting a contribution add/remove/edit attributes at runtime without
117
+ * re-registering.
118
+ */
119
+ interface AttributesSource {
120
+ subscribe: (listener: () => void) => () => void;
121
+ getSnapshot: () => AttributeDescriptor[];
122
+ }
123
+ declare const isAttributesSource: (attributes: AttributeDescriptor[] | AttributesSource) => attributes is AttributesSource;
124
+ interface KanbanRendererRow {
125
+ id: string;
126
+ title: string;
127
+ /**
128
+ * Optional navigation handle. When set, the renderer's default click handler
129
+ * routes through the contribution's onRowClick (which typically opens the
130
+ * resource via the workbench).
131
+ */
132
+ resource?: unknown;
133
+ attributes: Record<string, unknown>;
134
+ }
135
+ /** Board display settings. Order lives in the view's sorts. */
136
+ interface KanbanRendererSettings {
137
+ viewMode: ViewMode;
138
+ columnGrouping: string | typeof NO_GROUPING;
139
+ rowGrouping: string | typeof NO_GROUPING;
140
+ displayProperties: string[];
141
+ }
142
+ type KanbanRendererSavedView = CollectionSavedView<KanbanRendererSettings>;
143
+ type KanbanRendererCreateFieldType = "text" | "longtext" | "markdown" | "number" | "boolean" | "select" | "multi-select" | "files";
144
+ interface KanbanRendererCreateField {
145
+ id: string;
146
+ label: string;
147
+ description?: string;
148
+ placeholder?: string;
149
+ type: KanbanRendererCreateFieldType;
150
+ required?: boolean;
151
+ defaultValue?: unknown;
152
+ options?: EnumOption[];
153
+ /** `files` fields only. */
154
+ multiple?: boolean;
155
+ accept?: string;
156
+ }
157
+ /** Chrome copy is supplied by the caller so the dialog ships no strings of its own. */
158
+ interface KanbanRendererCreateLabels {
159
+ cancel: string;
160
+ properties: string;
161
+ submitError: string;
162
+ removeFile: string;
163
+ }
164
+ interface KanbanRendererCreateRowConfig {
165
+ title: string;
166
+ submitLabel: string;
167
+ fields: KanbanRendererCreateField[];
168
+ labels: KanbanRendererCreateLabels;
169
+ }
170
+ interface KanbanRendererCreateSubmission {
171
+ columnId: string;
172
+ columnAttributeId?: string;
173
+ /** Declared field values, excluding `files` fields — those arrive as `files`. */
174
+ values: Record<string, unknown>;
175
+ /** Every editable attribute the user set, keyed by attribute id. */
176
+ attributeValues: Record<string, unknown>;
177
+ files: File[];
178
+ }
179
+ declare const DEFAULT_KANBAN_RENDERER_SETTINGS: KanbanRendererSettings;
180
+ /**
181
+ * Look up an attribute by id from a contribution-provided descriptor list.
182
+ * Returns undefined for the "none" grouping or unknown ids.
183
+ */
184
+ declare const findAttribute: (attributes: AttributeDescriptor[], id: string | undefined) => AttributeDescriptor | undefined;
185
+ type KanbanRendererViewsSource = CollectionViewsSource<KanbanRendererSettings>;
186
+ //#endregion
187
+ //#region src/components/collection-view/use-collection-view-store.d.ts
188
+ /** The unsaved state of one renderer: the active view and the edits made on top of it. */
189
+ interface CollectionViewSnapshot<TSettings> {
190
+ settings: TSettings;
191
+ filter: ViewFilterGroup;
192
+ sorts: ViewSort[];
193
+ expandedGroups: Record<string, boolean>;
194
+ activeViewId: string;
195
+ }
196
+ type CollectionViewMenu = "filter";
197
+ interface CollectionViewStoreInitialState<TSettings> {
198
+ settings: TSettings;
199
+ filter?: ViewFilterGroup;
200
+ sorts?: ViewSort[];
201
+ }
202
+ interface CollectionViewStoreState<TSettings> extends CollectionViewSnapshot<TSettings> {
203
+ /** The view bar menu that is open. Screen state: never persisted. */
204
+ openMenu: CollectionViewMenu | null;
205
+ /** The root rule whose pill editor is open. Screen state: never persisted. */
206
+ openRuleIndex: number | null;
207
+ setOpenMenu: (menu: CollectionViewMenu | null) => void;
208
+ setOpenRuleIndex: (index: number | null) => void;
209
+ /** Adds a rule for the field and opens its pill editor. */
210
+ startRule: (field: AttributeDescriptor) => void;
211
+ /** Commits picker values; scalar values transfer editing to their bubble. */
212
+ selectRule: (rule: ViewFilterRule) => void;
213
+ setSettings: (settings: Partial<TSettings>) => void;
214
+ setFilter: (filter: ViewFilterGroup) => void;
215
+ setSorts: (sorts: ViewSort[]) => void;
216
+ setExpandedGroup: (groupId: string, isExpanded: boolean) => void;
217
+ activateView: (view: CollectionSavedView<TSettings>) => void;
218
+ reset: () => void;
219
+ }
220
+ declare const useCollectionViewStore: <TSettings, T>(storageKey: string, initialState: CollectionViewStoreInitialState<TSettings>, selector: (state: CollectionViewStoreState<TSettings>) => T) => T;
221
+ //#endregion
222
+ //#region src/components/collection-view/collection-view-fields.d.ts
223
+ declare const withTitleField: (attributes: AttributeDescriptor[]) => AttributeDescriptor[];
224
+ /**
225
+ * Read a typed attribute value out of a row. enum-multi normalizes to an array;
226
+ * single-valued kinds normalize to undefined when missing.
227
+ */
228
+ declare const getAttributeValue: (row: KanbanRendererRow, descriptor: AttributeDescriptor) => unknown;
229
+ declare const getAttributeStringValues: (row: KanbanRendererRow, descriptor: AttributeDescriptor) => string[];
230
+ //#endregion
231
+ export { CollectionSavedView as A, KanbanRendererSettings as C, findAttribute as D, ViewMode as E, ViewFilterGroup$1 as F, ViewFilterRule$1 as I, ViewSort$1 as L, CollectionViewsSource as M, EMPTY_VIEW_FILTER as N, isAttributesSource as O, ViewFilterCondition as P, ViewSortDirection as R, KanbanRendererSavedView as S, NO_GROUPING as T, KanbanRendererCreateField as _, CollectionViewStoreState as a, KanbanRendererCreateSubmission as b, AttributeDisplayDescriptor as c, AttributesSource as d, CollectionBadgeItem as f, EnumOptionsSource as g, EnumOptions as h, CollectionViewStoreInitialState as i, CollectionViewState as j, isEnumOptionsSource as k, AttributeKind as l, EnumOption as m, getAttributeValue as n, useCollectionViewStore as o, DEFAULT_KANBAN_RENDERER_SETTINGS as p, withTitleField as r, AttributeDescriptor as s, getAttributeStringValues as t, AttributeType as u, KanbanRendererCreateFieldType as v, KanbanRendererViewsSource as w, KanbanRendererRow as x, KanbanRendererCreateRowConfig as y };
232
+ export {};
@@ -0,0 +1,90 @@
1
+ import { S as e, _ as t, b as n, g as r, m as i, p as a, t as o, v as s } from "./collection-view-search-CpjJy76w.js";
2
+ import { Box as c } from "@chakra-ui/react";
3
+ import { jsx as l } from "react/jsx-runtime";
4
+ //#region src/components/collection-view/highlighted-text.tsx
5
+ var u = (e) => {
6
+ let { text: t, query: n } = e, r = o(t, n);
7
+ if (r.length === 0) return t;
8
+ let i = [], a = 0;
9
+ for (let [e, n] of r) e > a && i.push({
10
+ text: t.slice(a, e),
11
+ match: !1,
12
+ start: a
13
+ }), i.push({
14
+ text: t.slice(e, n),
15
+ match: !0,
16
+ start: e
17
+ }), a = n;
18
+ return a < t.length && i.push({
19
+ text: t.slice(a),
20
+ match: !1,
21
+ start: a
22
+ }), i.map((e) => e.match ? /* @__PURE__ */ l(c, {
23
+ as: "mark",
24
+ bg: "bg.warning",
25
+ color: "inherit",
26
+ borderRadius: "2xs",
27
+ children: e.text
28
+ }, e.start) : e.text);
29
+ }, d = (t, n, r) => {
30
+ if (r.kind !== "enum" && r.kind !== "enum-multi") return 0;
31
+ let i = e(r), a = (e) => {
32
+ if (e === void 0) return i.length;
33
+ let t = i.findIndex((t) => t.value === e);
34
+ return t === -1 ? i.length + 1 : t;
35
+ };
36
+ return a(t) - a(n);
37
+ }, f = (e) => s(e)?.getTime() ?? NaN, p = (e, n, i) => {
38
+ let a = t(e, i), o = t(n, i);
39
+ if (i.compare) return i.compare(a, o);
40
+ if (i.type.kind === "date") return f(a) - f(o);
41
+ if (i.type.kind === "number") return Number(a) - Number(o);
42
+ if (i.type.kind === "enum") {
43
+ let [t] = r(e, i), [a] = r(n, i);
44
+ return d(t, a, i.type);
45
+ }
46
+ return String(a).localeCompare(String(o), void 0, {
47
+ numeric: !0,
48
+ sensitivity: "base"
49
+ });
50
+ }, m = (e, n) => {
51
+ if (n.type.kind === "date") return s(t(e, n)) === void 0;
52
+ let [i] = r(e, n);
53
+ return i === void 0 || n.type.kind === "number" && !Number.isFinite(Number(i));
54
+ }, h = (e, t) => {
55
+ let n = i(t, e.attributeId);
56
+ if (!n || !a(n)) return;
57
+ let r = e.direction === "asc" ? 1 : -1;
58
+ return (e, t) => {
59
+ let i = m(e, n), a = m(t, n);
60
+ return i || a ? Number(i) - Number(a) : r * p(e, t, n);
61
+ };
62
+ }, g = (e, t, n) => {
63
+ let r = t[0], i = r ? h(r, n) : void 0;
64
+ return i ? [...e].sort(i) : e;
65
+ }, _ = (e) => `No ${e.label.toLowerCase()}`, v = (e, t) => e === _(t) ? e : t.type.kind === "enum" || t.type.kind === "enum-multi" ? n(t.type, e) : e, y = (t, n) => {
66
+ if (t !== void 0) return n.type.kind !== "enum" && n.type.kind !== "enum-multi" || e(n.type).some((e) => e.value === t) ? t : void 0;
67
+ }, b = (e, t) => {
68
+ let [n] = r(e, t);
69
+ return y(n, t) ?? _(t);
70
+ }, x = (e, t, n) => {
71
+ let r = _(n);
72
+ if (e === r || t === r) return Number(e === r) - Number(t === r);
73
+ let i = d(e, t, n.type);
74
+ return i === 0 ? e.localeCompare(t, void 0, { numeric: !0 }) : i;
75
+ }, S = (e, t, n = []) => {
76
+ let r = new Map(n.map((e) => [e, []]));
77
+ for (let n of e) {
78
+ let e = b(n, t), i = r.get(e);
79
+ i ? i.push(n) : r.set(e, [n]);
80
+ }
81
+ return [...r.entries()].sort(([e], [n]) => x(e, n, t)).map(([e, n]) => ({
82
+ key: e,
83
+ label: v(e, t),
84
+ rows: n
85
+ }));
86
+ };
87
+ //#endregion
88
+ export { u as a, g as i, b as n, S as r, _ as t };
89
+
90
+ //# sourceMappingURL=collection-view-grouping-D4nZk8-3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-view-grouping-D4nZk8-3.js","names":["Box","findSearchRanges","HighlightedTextProps","text","query","HighlightedText","props","ranges","length","parts","Array","match","start","cursor","end","push","slice","map","part","ViewSort","getEnumOptions","AttributeDescriptor","AttributeType","KanbanRendererRow","canSortField","findField","getAttributeStringValues","getAttributeValue","parseViewDate","compareEnumValues","left","right","type","kind","options","toIndex","value","undefined","length","index","findIndex","option","toTime","getTime","Number","NaN","compareFieldValues","a","b","field","compare","leftValue","rightValue","String","localeCompare","numeric","sensitivity","isEmpty","row","isFinite","compileSort","sort","fields","attributeId","direction","leftEmpty","rightEmpty","sortRowsByView","rows","TRow","sorts","enumOptionLabel","getEnumOptions","AttributeDescriptor","KanbanRendererRow","getAttributeStringValues","compareEnumValues","CollectionRowGroup","key","label","rows","TRow","emptyGroupKey","field","toLowerCase","groupLabel","type","kind","resolveGroupValue","value","undefined","some","option","groupKey","row","compareGroupKeys","left","right","empty","Number","ordered","localeCompare","numeric","groupRowsByField","knownKeys","groups","Map","map","group","get","push","set","entries","sort","groupRows","getEnumOptions","getAttributeValue","enumOptionLabel","getAttributeStringValues","findField","canSortField","findSearchRanges","parseViewDate","HighlightedText","props","ranges","text","query","parts","cursor","start","end","part","Box","compareEnumValues","type","options","toIndex","value","index","option","left","right","toTime","compareFieldValues","a","field","b","leftValue","rightValue","isEmpty","row","compileSort","fields","sort","direction","leftEmpty","rightEmpty","sortRowsByView","sorts","compare","rows","emptyGroupKey","groupLabel","key","resolveGroupValue","groupKey","compareGroupKeys","empty","ordered","groupRowsByField","groups","knownKeys","group","groupRows"],"sources":["../src/components/collection-view/highlighted-text.tsx","../src/components/collection-view/collection-view-sort.ts","../src/components/collection-view/collection-view-grouping.ts"],"sourcesContent":["import { Box } from \"@chakra-ui/react\";\nimport { findSearchRanges } from \"./collection-view-search\";\n\nexport interface HighlightedTextProps {\n text: string;\n /** The view's search text. Every match in `text` is marked. */\n query: string;\n}\n\nexport const HighlightedText = (props: HighlightedTextProps) => {\n const { text, query } = props;\n const ranges = findSearchRanges(text, query);\n if (ranges.length === 0) return text;\n const parts: Array<{ text: string; match: boolean; start: number }> = [];\n let cursor = 0;\n for (const [start, end] of ranges) {\n if (start > cursor) parts.push({ text: text.slice(cursor, start), match: false, start: cursor });\n parts.push({ text: text.slice(start, end), match: true, start });\n cursor = end;\n }\n if (cursor < text.length) parts.push({ text: text.slice(cursor), match: false, start: cursor });\n\n return parts.map((part) =>\n part.match ? (\n <Box as=\"mark\" key={part.start} bg=\"bg.warning\" color=\"inherit\" borderRadius=\"2xs\">\n {part.text}\n </Box>\n ) : (\n part.text\n ),\n );\n};\n","import type { ViewSort } from \"@pstdio/sdk/extensions\";\nimport { getEnumOptions } from \"../kanban-renderer/kanban-renderer-enum-helpers\";\nimport type { AttributeDescriptor, AttributeType, KanbanRendererRow } from \"../kanban-renderer/types\";\nimport {\n canSortField,\n findField,\n getAttributeStringValues,\n getAttributeValue,\n parseViewDate,\n} from \"./collection-view-fields\";\n\n/** Option order: declared options first, unknown values after them, missing values last. */\nexport const compareEnumValues = (left: string | undefined, right: string | undefined, type: AttributeType) => {\n if (type.kind !== \"enum\" && type.kind !== \"enum-multi\") return 0;\n const options = getEnumOptions(type);\n const toIndex = (value: string | undefined) => {\n if (value === undefined) return options.length;\n const index = options.findIndex((option) => option.value === value);\n return index === -1 ? options.length + 1 : index;\n };\n return toIndex(left) - toIndex(right);\n};\n\nconst toTime = (value: unknown) => parseViewDate(value)?.getTime() ?? Number.NaN;\n\nconst compareFieldValues = (a: KanbanRendererRow, b: KanbanRendererRow, field: AttributeDescriptor) => {\n const left = getAttributeValue(a, field);\n const right = getAttributeValue(b, field);\n if (field.compare) return field.compare(left, right);\n if (field.type.kind === \"date\") return toTime(left) - toTime(right);\n if (field.type.kind === \"number\") return Number(left) - Number(right);\n if (field.type.kind === \"enum\") {\n const [leftValue] = getAttributeStringValues(a, field);\n const [rightValue] = getAttributeStringValues(b, field);\n return compareEnumValues(leftValue, rightValue, field.type);\n }\n return String(left).localeCompare(String(right), undefined, { numeric: true, sensitivity: \"base\" });\n};\n\nconst isEmpty = (row: KanbanRendererRow, field: AttributeDescriptor) => {\n if (field.type.kind === \"date\") return parseViewDate(getAttributeValue(row, field)) === undefined;\n const [value] = getAttributeStringValues(row, field);\n if (value === undefined) return true;\n if (field.type.kind === \"number\") return !Number.isFinite(Number(value));\n return false;\n};\n\nconst compileSort = (sort: ViewSort, fields: AttributeDescriptor[]) => {\n const field = findField(fields, sort.attributeId);\n if (!field || !canSortField(field)) return undefined;\n const direction = sort.direction === \"asc\" ? 1 : -1;\n // Rows without a value stay last in both directions.\n return (a: KanbanRendererRow, b: KanbanRendererRow) => {\n const leftEmpty = isEmpty(a, field);\n const rightEmpty = isEmpty(b, field);\n if (leftEmpty || rightEmpty) return Number(leftEmpty) - Number(rightEmpty);\n return direction * compareFieldValues(a, b, field);\n };\n};\n\n/** One field decides the order; ties keep their incoming order. */\nexport const sortRowsByView = <TRow extends KanbanRendererRow>(\n rows: TRow[],\n sorts: ViewSort[],\n fields: AttributeDescriptor[],\n) => {\n const sort = sorts[0];\n const compare = sort ? compileSort(sort, fields) : undefined;\n return compare ? [...rows].sort(compare) : rows;\n};\n","import { enumOptionLabel, getEnumOptions } from \"../kanban-renderer/kanban-renderer-enum-helpers\";\nimport type { AttributeDescriptor, KanbanRendererRow } from \"../kanban-renderer/types\";\nimport { getAttributeStringValues } from \"./collection-view-fields\";\nimport { compareEnumValues } from \"./collection-view-sort\";\n\nexport interface CollectionRowGroup<TRow extends KanbanRendererRow = KanbanRendererRow> {\n key: string;\n label: string;\n rows: TRow[];\n}\n\nexport const emptyGroupKey = (field: AttributeDescriptor) => `No ${field.label.toLowerCase()}`;\n\nexport const groupLabel = (key: string, field: AttributeDescriptor) => {\n if (key === emptyGroupKey(field)) return key;\n if (field.type.kind === \"enum\" || field.type.kind === \"enum-multi\") return enumOptionLabel(field.type, key);\n return key;\n};\n\n// Drops values that are no longer a declared option, so rows pointing at a removed\n// status join the \"No <field>\" group instead of an orphaned group for the deleted value.\nconst resolveGroupValue = (value: string | undefined, field: AttributeDescriptor) => {\n if (value === undefined) return undefined;\n if (field.type.kind !== \"enum\" && field.type.kind !== \"enum-multi\") return value;\n return getEnumOptions(field.type).some((option) => option.value === value) ? value : undefined;\n};\n\n/** One group per row. A multi-value field is not offered for grouping, so its first value decides. */\nexport const groupKey = (row: KanbanRendererRow, field: AttributeDescriptor) => {\n const [value] = getAttributeStringValues(row, field);\n return resolveGroupValue(value, field) ?? emptyGroupKey(field);\n};\n\n/** Option fields follow option order, other fields go A to Z, and the \"No <field>\" group comes last. */\nexport const compareGroupKeys = (left: string, right: string, field: AttributeDescriptor) => {\n const empty = emptyGroupKey(field);\n if (left === empty || right === empty) return Number(left === empty) - Number(right === empty);\n const ordered = compareEnumValues(left, right, field.type);\n if (ordered !== 0) return ordered;\n return left.localeCompare(right, undefined, { numeric: true });\n};\n\nexport const groupRowsByField = <TRow extends KanbanRendererRow>(\n rows: TRow[],\n field: AttributeDescriptor,\n knownKeys: string[] = [],\n): CollectionRowGroup<TRow>[] => {\n const groups = new Map<string, TRow[]>(knownKeys.map((key) => [key, []]));\n for (const row of rows) {\n const key = groupKey(row, field);\n const group = groups.get(key);\n if (group) group.push(row);\n else groups.set(key, [row]);\n }\n return [...groups.entries()]\n .sort(([left], [right]) => compareGroupKeys(left, right, field))\n .map(([key, groupRows]) => ({ key, label: groupLabel(key, field), rows: groupRows }));\n};\n"],"mappings":"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"}