@crewlethq/ui 0.34.2 → 0.35.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 (96) hide show
  1. package/README.md +47 -0
  2. package/dist/AppShell/index.js +2 -2
  3. package/dist/ArtifactBar/ArtifactBar.d.ts +6 -0
  4. package/dist/ArtifactBar/index.d.ts +1 -0
  5. package/dist/ArtifactBar/index.js +2 -2
  6. package/dist/{ArtifactBar-C462VD4C.css → ArtifactBar-HV5AT4RG.css} +39 -2
  7. package/dist/ArtifactCard/index.d.ts +1 -0
  8. package/dist/ArtifactCard/index.js +2 -2
  9. package/dist/{ArtifactCard-XHRLAL7C.css → ArtifactCard-OGTMO2KI.css} +15 -16
  10. package/dist/ArtifactKindIllustration/ArtifactKindIllustration.d.ts +14 -1
  11. package/dist/ArtifactKindIllustration/index.d.ts +1 -0
  12. package/dist/ArtifactKindIllustration/index.js +1 -1
  13. package/dist/{ArtifactKindIllustration-4NG6IE3C.css → ArtifactKindIllustration-5FPRJQ3P.css} +32 -26
  14. package/dist/ArtifactKinds-CVD3FCYO.css +15 -0
  15. package/dist/AvatarPicker/index.js +2 -2
  16. package/dist/CanvasBoard/CanvasBoard.d.ts +30 -12
  17. package/dist/CanvasBoard/index.js +1 -1
  18. package/dist/ChangeMark/index.js +2 -2
  19. package/dist/ChoiceList/ChoiceList.d.ts +7 -1
  20. package/dist/ChoiceList/index.js +1 -1
  21. package/dist/{ChoiceList-2PQJS4DX.css → ChoiceList-RBL7FNQL.css} +39 -0
  22. package/dist/DataTable/index.js +2 -2
  23. package/dist/DataView/index.js +3 -3
  24. package/dist/DocTabsEditor/index.js +2 -2
  25. package/dist/{Editor-JMRD6AZW.js → Editor-BVQHBF7L.js} +4 -4
  26. package/dist/FileDrop/FileDrop.d.ts +64 -0
  27. package/dist/FileDrop/index.d.ts +3 -0
  28. package/dist/FileDrop/index.js +4 -0
  29. package/dist/FileDrop-WE7BNZGV.css +80 -0
  30. package/dist/FindingList/FindingList.d.ts +35 -0
  31. package/dist/FindingList/index.d.ts +3 -0
  32. package/dist/FindingList/index.js +3 -0
  33. package/dist/FindingList-PMYA2R6Q.css +71 -0
  34. package/dist/HuePicker/HuePicker.d.ts +9 -0
  35. package/dist/HuePicker/index.d.ts +1 -1
  36. package/dist/HuePicker/index.js +1 -1
  37. package/dist/Markdown/index.js +1 -1
  38. package/dist/Menu/Menu.d.ts +6 -1
  39. package/dist/Menu/index.js +1 -1
  40. package/dist/RichEditor/index.js +3 -3
  41. package/dist/RichEditorMarkdown/index.js +3 -3
  42. package/dist/SheetColumnEditor/SheetColumnEditor.d.ts +94 -0
  43. package/dist/SheetColumnEditor/index.d.ts +3 -0
  44. package/dist/SheetColumnEditor/index.js +23 -0
  45. package/dist/SheetColumnEditor-BO2HKFRB.css +73 -0
  46. package/dist/SheetGrid/SheetGrid.d.ts +19 -1
  47. package/dist/SheetGrid/index.js +5 -4
  48. package/dist/{SheetGrid-GDPCCZ4V.css → SheetGrid-YRDXTIIZ.css} +48 -0
  49. package/dist/SheetModel/index.js +1 -1
  50. package/dist/SheetModel/labels.d.ts +6 -0
  51. package/dist/SlideFilmstrip/SlideFilmstrip.d.ts +30 -1
  52. package/dist/SlideFilmstrip/index.js +5 -1
  53. package/dist/{SlideFilmstrip-5U6Z55GS.css → SlideFilmstrip-4ITZLMO4.css} +49 -0
  54. package/dist/SourceEditor/SourceEditor.d.ts +62 -0
  55. package/dist/SourceEditor/index.d.ts +3 -0
  56. package/dist/SourceEditor/index.js +3 -0
  57. package/dist/SourceEditor-G2C3STPX.css +160 -0
  58. package/dist/ThemeGallery/ThemeGallery.d.ts +10 -3
  59. package/dist/ThemeGallery/index.js +1 -1
  60. package/dist/{ThemeGallery-NITASULQ.css → ThemeGallery-HSUKBDWY.css} +9 -0
  61. package/dist/{chunk-UXXJDIYE.js → chunk-22CQQDFW.js} +1 -1
  62. package/dist/{chunk-EQ7P7DGN.js → chunk-3VD3G3IL.js} +2 -2
  63. package/dist/{chunk-EWAFN2IU.js → chunk-5VCG26CV.js} +281 -234
  64. package/dist/{chunk-7T5LSYZ2.js → chunk-7UYONN6K.js} +4 -3
  65. package/dist/{chunk-KK5OYNAF.js → chunk-BAKPMH3S.js} +8 -4
  66. package/dist/{chunk-3EB2AV3H.js → chunk-BUVCV3WI.js} +1 -1
  67. package/dist/{chunk-4B6LSFDS.js → chunk-CSQTDGTS.js} +1 -1
  68. package/dist/{chunk-WPX6A5BV.js → chunk-E35CMI5D.js} +22 -6
  69. package/dist/{chunk-OMLACFPP.js → chunk-FJJHD73S.js} +1 -1
  70. package/dist/{chunk-SGHIAGPR.js → chunk-HAKTRTRP.js} +1 -1
  71. package/dist/chunk-MAD6WPER.js +342 -0
  72. package/dist/{chunk-ZADVT7IL.js → chunk-NKZCJRKO.js} +9 -4
  73. package/dist/chunk-NTB5LRRD.js +52 -0
  74. package/dist/{chunk-VI5FWDZW.js → chunk-OOMXLJ3S.js} +4 -1
  75. package/dist/{chunk-UOR7TOAU.js → chunk-OSFA5ROT.js} +86 -1
  76. package/dist/{chunk-T46NR3WP.js → chunk-PGZ2R3ON.js} +1 -1
  77. package/dist/chunk-PI7HKLIT.js +245 -0
  78. package/dist/{chunk-YZFLH463.js → chunk-QEFHYUCY.js} +1 -1
  79. package/dist/{chunk-NPQWKZTB.js → chunk-QFNWMWWY.js} +2 -2
  80. package/dist/{chunk-C7DKX453.js → chunk-QH6VYJ3X.js} +1 -1
  81. package/dist/{chunk-XV32RKQZ.js → chunk-QIXMNOBD.js} +67 -6
  82. package/dist/{chunk-GPHWCK7M.js → chunk-SUXP4OBT.js} +6 -3
  83. package/dist/{chunk-TRQCHF2F.js → chunk-SWD4QBL6.js} +1 -1
  84. package/dist/chunk-THDGBD6Y.js +154 -0
  85. package/dist/{chunk-C2FRTWKO.js → chunk-W3UVOZKL.js} +3 -2
  86. package/dist/{chunk-UDQW6UK7.js → chunk-W7QBBKUE.js} +34 -7
  87. package/dist/{chunk-QBXZLGK2.js → chunk-ZU3DUXD2.js} +3 -2
  88. package/dist/generated/index.d.ts +4 -0
  89. package/dist/generated/index.js +56 -52
  90. package/dist/index.js +56 -52
  91. package/dist/styles.css +652 -51
  92. package/dist/utils/csv.d.ts +35 -0
  93. package/dist/utils/index.d.ts +2 -1
  94. package/dist/utils/index.js +1 -1
  95. package/package.json +3 -3
  96. /package/dist/{chunk-VFRSSP3W.js → chunk-3WCNHYXE.js} +0 -0
@@ -3,18 +3,19 @@ import { Tag } from './chunk-GFV4LJHH.js';
3
3
  import { StatusDot } from './chunk-WIATRXEJ.js';
4
4
  import { CursorFlag } from './chunk-3BYIWOTV.js';
5
5
  import { presenceHueClass } from './chunk-I6POAB4V.js';
6
- import { ChangeMark } from './chunk-SGHIAGPR.js';
7
- import { isEmptyValue, displayText, ratingMax, findPerson, findOption, statusTone, editText, textMax, sheetLabels, normalizeValue, sameValue, spokenText, drawnColumns, fitSamples, fittedWidth, columnWidth, SHEET_COLUMN_BOUNDS, sortRows, SHEET_ROW_HEIGHT, cellKey, columnOffsets, SHEET_ROW_NUMBER_WIDTH, rangeOf, presenceByCell, presenceByRow, presenceByColumn, scrollToRow, planClear, visibleRows, cellDescription, nextSort, rowName, inRange, hasPrevious, flagText, toTSV, planPaste, parseTSV, isMod, moveFrom, isTypingKey, editorSide, optionHue, parseText, liveOptions, checkValue } from './chunk-VI5FWDZW.js';
6
+ import { ChangeMark } from './chunk-HAKTRTRP.js';
7
+ import { isEmptyValue, displayText, ratingMax, findPerson, findOption, statusTone, editText, textMax, sheetLabels, normalizeValue, sameValue, spokenText, drawnColumns, fitSamples, fittedWidth, columnWidth, SHEET_COLUMN_BOUNDS, sortRows, SHEET_ROW_HEIGHT, cellKey, columnOffsets, SHEET_ROW_NUMBER_WIDTH, rangeOf, presenceByCell, presenceByRow, presenceByColumn, scrollToRow, planClear, visibleRows, cellDescription, nextSort, rowName, inRange, hasPrevious, flagText, toTSV, planPaste, parseTSV, isMod, moveFrom, isTypingKey, editorSide, optionHue, parseText, liveOptions, checkValue } from './chunk-OOMXLJ3S.js';
8
8
  import { presenceLabels } from './chunk-Z7JYG5TF.js';
9
+ import { Popover } from './chunk-K3IJQNB7.js';
9
10
  import { Tooltip } from './chunk-6ZJ3N7QI.js';
10
11
  import { Avatar } from './chunk-PZKHBB7D.js';
11
- import { Menu } from './chunk-3EB2AV3H.js';
12
+ import { Menu } from './chunk-BUVCV3WI.js';
12
13
  import { isComposing } from './chunk-R25RGPUB.js';
13
14
  import { Button } from './chunk-OG6CL3EB.js';
14
15
  import { IconButton } from './chunk-4FQT6ZAY.js';
15
16
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
16
17
  import { cx } from './chunk-ODEQTPFM.js';
17
- import './SheetGrid-GDPCCZ4V.css';
18
+ import './SheetGrid-YRDXTIIZ.css';
18
19
  import { useId, useRef, useMemo, useState, useLayoutEffect, useEffect, useCallback } from 'react';
19
20
  import { CheckGlyph, StarGlyph, CircleAlertGlyph, ArrowUpGlyph, ArrowDownGlyph, TrashGlyph, EllipsisGlyph, RotateCcwClockGlyph, PlusGlyph } from '@crewlethq/icons/glyphs';
20
21
  import { isCrewletCharacter } from '@crewlethq/icons/characters';
@@ -641,6 +642,10 @@ function SheetGrid({
641
642
  fit = "container",
642
643
  onDeleteRow,
643
644
  onDismissRefused,
645
+ renderColumnEditor,
646
+ settingsColumn,
647
+ onSettingsColumnChange,
648
+ onAddColumn,
644
649
  locale,
645
650
  className,
646
651
  labels: labelOverrides,
@@ -706,6 +711,14 @@ function SheetGrid({
706
711
  const rows = useMemo(() => sortRows(columns, given, sort, context), [columns, given, sort, context]);
707
712
  const editable = mode === "edit" && onEdit !== void 0;
708
713
  const rowActions = mode === "edit" && onDeleteRow !== void 0;
714
+ const columnSettings = mode === "edit" && renderColumnEditor !== void 0;
715
+ const addsColumns = mode === "edit" && onAddColumn !== void 0;
716
+ const [ownSettingsColumn, setOwnSettingsColumn] = useState(null);
717
+ const openColumn = settingsColumn !== void 0 ? settingsColumn : ownSettingsColumn;
718
+ const setOpenColumn = (field) => {
719
+ if (settingsColumn === void 0) setOwnSettingsColumn(field);
720
+ onSettingsColumnChange?.(field);
721
+ };
709
722
  const scroller = useRef(null);
710
723
  const probe = useRef(null);
711
724
  const grid = useRef(null);
@@ -971,7 +984,7 @@ function SheetGrid({
971
984
  if (conflictText) say(conflictText);
972
985
  }, [conflictText, say]);
973
986
  const onCopy = (event) => {
974
- if (editing || !range) return;
987
+ if (editing || !range || fromElsewhere(event)) return;
975
988
  event.preventDefault();
976
989
  event.clipboardData.setData("text/plain", toTSV(columns, rows, range, context));
977
990
  };
@@ -986,12 +999,12 @@ function SheetGrid({
986
999
  refuseLocally(plan.refused);
987
1000
  }, [columns, context, editable, emit, labels.forbidden, range, refuseLocally, rows, say]);
988
1001
  const onCut = (event) => {
989
- if (editing || !range) return;
1002
+ if (editing || !range || fromElsewhere(event)) return;
990
1003
  onCopy(event);
991
1004
  clearRange();
992
1005
  };
993
1006
  const onPaste = (event) => {
994
- if (editing || !range) return;
1007
+ if (editing || !range || fromElsewhere(event)) return;
995
1008
  event.preventDefault();
996
1009
  if (!editable) {
997
1010
  say(labels.forbidden);
@@ -1003,7 +1016,7 @@ function SheetGrid({
1003
1016
  };
1004
1017
  const page = Math.max(1, Math.floor(Math.max(viewport - headerHeight, rowHeight) / rowHeight) - 1);
1005
1018
  const onKeyDown = (event) => {
1006
- if (editing || isComposing(event) || !current || !currentCell) return;
1019
+ if (editing || isComposing(event) || !current || !currentCell || fromElsewhere(event)) return;
1007
1020
  if (event.target.closest(".crewlet-sheet-grid__row-actions")) return;
1008
1021
  const column = columns[current.col];
1009
1022
  const mod = isMod(event);
@@ -1058,7 +1071,10 @@ function SheetGrid({
1058
1071
  return;
1059
1072
  }
1060
1073
  if (current.row === HEADER) {
1061
- if ((event.key === "Enter" || event.key === " ") && column && onSortChange) {
1074
+ if ((event.key === "Enter" || event.key === " ") && column && columnSettings) {
1075
+ event.preventDefault();
1076
+ setOpenColumn(column.field);
1077
+ } else if ((event.key === "Enter" || event.key === " ") && column && onSortChange) {
1062
1078
  event.preventDefault();
1063
1079
  onSortChange(nextSort(sort, column.field));
1064
1080
  }
@@ -1168,249 +1184,277 @@ function SheetGrid({
1168
1184
  };
1169
1185
  return /* @__PURE__ */ jsxs("div", { className: cx("crewlet-sheet-grid", mode === "read" && "crewlet-sheet-grid--read", fit === "content" && "crewlet-sheet-grid--fit-content", className), children: [
1170
1186
  /* @__PURE__ */ jsx("div", { ref: probe, className: "crewlet-sheet-grid__probe", "aria-hidden": "true" }),
1171
- /* @__PURE__ */ jsx(
1187
+ /* @__PURE__ */ jsxs(
1172
1188
  "div",
1173
1189
  {
1174
1190
  ref: scroller,
1175
- className: cx("crewlet-sheet-grid__scroller", scrolledAcross && "crewlet-sheet-grid__scroller--across"),
1191
+ className: cx("crewlet-sheet-grid__scroller", scrolledAcross && "crewlet-sheet-grid__scroller--across", addsColumns && "crewlet-sheet-grid__scroller--add-column"),
1176
1192
  style: scrollerStyle,
1177
1193
  onScroll: (event) => {
1178
1194
  setScrollTop(event.currentTarget.scrollTop);
1179
1195
  setScrolledAcross(event.currentTarget.scrollLeft > 0);
1180
1196
  },
1181
- children: /* @__PURE__ */ jsxs(
1182
- "div",
1183
- {
1184
- ref: grid,
1185
- role: "grid",
1186
- "aria-label": label,
1187
- "aria-rowcount": rows.length + 1,
1188
- "aria-colcount": columns.length + 1,
1189
- "aria-readonly": !editable || void 0,
1190
- "aria-multiselectable": "true",
1191
- "aria-describedby": helpId,
1192
- tabIndex: -1,
1193
- className: cx("crewlet-sheet-grid__grid", range && (range.top !== range.bottom || range.left !== range.right) && "crewlet-sheet-grid__grid--range"),
1194
- style,
1195
- onKeyDown,
1196
- onCopy,
1197
- onCut,
1198
- onPaste,
1199
- children: [
1200
- /* @__PURE__ */ jsx("div", { role: "rowgroup", className: "crewlet-sheet-grid__head", children: /* @__PURE__ */ jsxs("div", { role: "row", "aria-rowindex": 1, className: "crewlet-sheet-grid__row crewlet-sheet-grid__row--head", children: [
1201
- /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-colindex": 1, className: cx("crewlet-sheet-grid__corner", lastFrozen < 0 && "crewlet-sheet-grid__frozen-edge"), children: /* @__PURE__ */ jsx(VisuallyHidden, { children: labels.rowHeader }) }),
1202
- columns.map((column, col) => {
1203
- const position = { row: HEADER, col };
1204
- const isActive = current?.row === HEADER && current.col === col;
1205
- const sorted = sort?.field === column.field ? sort.direction : void 0;
1206
- const here = onColumns.get(column.field) ?? [];
1207
- return (
1208
- // eslint-disable-next-line jsx-a11y/click-events-have-key-events -- keys reach the grid's own handler, which sorts on Enter and Space
1209
- /* @__PURE__ */ jsxs(
1210
- "div",
1211
- {
1212
- id: cellDomId(id, position),
1213
- role: "columnheader",
1214
- "aria-colindex": col + 2,
1215
- "aria-sort": sorted ?? (onSortChange ? "none" : void 0),
1216
- tabIndex: isActive ? 0 : -1,
1217
- className: cx(
1218
- "crewlet-sheet-grid__header",
1219
- `crewlet-sheet-grid__header--${column.type}`,
1220
- column.frozen && "crewlet-sheet-grid__cell--frozen",
1221
- col === lastFrozen && "crewlet-sheet-grid__frozen-edge",
1222
- isActive && "crewlet-sheet-grid__header--active"
1223
- ),
1224
- style: column.frozen ? { insetInlineStart: `${offsets[col]}px` } : void 0,
1225
- onMouseDown: (event) => onCellMouseDown(event, { rowId: null, field: column.field }),
1226
- onFocus: (event) => {
1227
- if (event.target === event.currentTarget) onCellFocus({ rowId: null, field: column.field });
1228
- },
1229
- onClick: () => {
1230
- if (onSortChange) onSortChange(nextSort(sort, column.field));
1231
- },
1232
- title: onSortChange ? labels.sortBy(column.label) : column.label,
1233
- children: [
1234
- /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__header-label", children: column.label }),
1235
- /* @__PURE__ */ jsx(VisuallyHidden, { children: `, ${labels.types[column.type]}` }),
1236
- sorted ? /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__sort", "aria-hidden": "true", children: sorted === "ascending" ? /* @__PURE__ */ jsx(ArrowUpGlyph, { size: "xs" }) : /* @__PURE__ */ jsx(ArrowDownGlyph, { size: "xs" }) }) : null,
1237
- here.length > 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__header-presence", "aria-hidden": "true", children: here.map((entry) => /* @__PURE__ */ jsx(Avatar, { name: entry.name, kind: entry.kind, size: 16, decorative: true, className: cx("crewlet-sheet-grid__header-badge", presenceHueClass(entry.hue)) }, entry.id)) }) : null
1238
- ]
1239
- },
1240
- column.field
1241
- )
1242
- );
1243
- })
1244
- ] }) }),
1245
- /* @__PURE__ */ jsx("div", { role: "rowgroup", className: "crewlet-sheet-grid__body", style: { height: `${rows.length * rowHeight}px` }, children: drawn.map((index) => {
1246
- const row = rows[index];
1247
- const rowPresence = onRows.get(row.id);
1248
- const rowNameId = `${id}-r${index}-name`;
1249
- const rowSelected = range !== null && index >= range.top && index <= range.bottom;
1250
- const actionsLabel = labels.rowActions(index + 1);
1251
- return /* @__PURE__ */ jsxs(
1252
- "div",
1253
- {
1254
- role: "row",
1255
- "aria-rowindex": index + 2,
1256
- className: "crewlet-sheet-grid__row",
1257
- style: { top: `${index * rowHeight}px` },
1258
- "data-row-id": row.id,
1259
- children: [
1197
+ children: [
1198
+ /* @__PURE__ */ jsxs(
1199
+ "div",
1200
+ {
1201
+ ref: grid,
1202
+ role: "grid",
1203
+ "aria-label": label,
1204
+ "aria-rowcount": rows.length + 1,
1205
+ "aria-colcount": columns.length + 1,
1206
+ "aria-readonly": !editable || void 0,
1207
+ "aria-multiselectable": "true",
1208
+ "aria-describedby": helpId,
1209
+ tabIndex: -1,
1210
+ className: cx("crewlet-sheet-grid__grid", range && (range.top !== range.bottom || range.left !== range.right) && "crewlet-sheet-grid__grid--range"),
1211
+ style,
1212
+ onKeyDown,
1213
+ onCopy,
1214
+ onCut,
1215
+ onPaste,
1216
+ children: [
1217
+ /* @__PURE__ */ jsx("div", { role: "rowgroup", className: "crewlet-sheet-grid__head", children: /* @__PURE__ */ jsxs("div", { role: "row", "aria-rowindex": 1, className: "crewlet-sheet-grid__row crewlet-sheet-grid__row--head", children: [
1218
+ /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-colindex": 1, className: cx("crewlet-sheet-grid__corner", lastFrozen < 0 && "crewlet-sheet-grid__frozen-edge"), children: /* @__PURE__ */ jsx(VisuallyHidden, { children: labels.rowHeader }) }),
1219
+ columns.map((column, col) => {
1220
+ const position = { row: HEADER, col };
1221
+ const isActive = current?.row === HEADER && current.col === col;
1222
+ const sorted = sort?.field === column.field ? sort.direction : void 0;
1223
+ const here = onColumns.get(column.field) ?? [];
1224
+ const header = (
1225
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events -- keys reach the grid's own handler, which sorts or opens the column's settings on Enter and Space
1260
1226
  /* @__PURE__ */ jsxs(
1261
1227
  "div",
1262
1228
  {
1263
- role: "rowheader",
1264
- "aria-colindex": 1,
1265
- "aria-labelledby": rowNameId,
1229
+ id: cellDomId(id, position),
1230
+ role: "columnheader",
1231
+ "aria-colindex": col + 2,
1232
+ "aria-sort": sorted ?? (onSortChange ? "none" : void 0),
1233
+ tabIndex: isActive ? 0 : -1,
1266
1234
  className: cx(
1267
- "crewlet-sheet-grid__rownum",
1268
- lastFrozen < 0 && "crewlet-sheet-grid__frozen-edge",
1269
- rowActions && "crewlet-sheet-grid__rownum--actions",
1270
- rowSelected && "crewlet-sheet-grid__rownum--selected",
1271
- rowMenu === row.id && "crewlet-sheet-grid__rownum--open",
1272
- rowPresence && "crewlet-sheet-grid__rownum--present",
1273
- rowPresence && presenceHueClass(rowPresence.hue)
1235
+ "crewlet-sheet-grid__header",
1236
+ `crewlet-sheet-grid__header--${column.type}`,
1237
+ column.frozen && "crewlet-sheet-grid__cell--frozen",
1238
+ col === lastFrozen && "crewlet-sheet-grid__frozen-edge",
1239
+ isActive && "crewlet-sheet-grid__header--active",
1240
+ columnSettings && "crewlet-sheet-grid__header--settings",
1241
+ openColumn === column.field && "crewlet-sheet-grid__header--open"
1274
1242
  ),
1243
+ style: column.frozen ? { insetInlineStart: `${offsets[col]}px` } : void 0,
1244
+ onMouseDown: (event) => {
1245
+ if (!fromElsewhere(event)) onCellMouseDown(event, { rowId: null, field: column.field });
1246
+ },
1247
+ onFocus: (event) => {
1248
+ if (event.target === event.currentTarget) onCellFocus({ rowId: null, field: column.field });
1249
+ },
1250
+ onClick: (event) => {
1251
+ if (fromElsewhere(event)) return;
1252
+ if (columnSettings) setOpenColumn(openColumn === column.field ? null : column.field);
1253
+ else if (onSortChange) onSortChange(nextSort(sort, column.field));
1254
+ },
1255
+ title: columnSettings ? labels.columnSettings(column.label) : onSortChange ? labels.sortBy(column.label) : column.label,
1275
1256
  children: [
1276
- /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__rownum-number", "aria-hidden": "true", children: index + 1 }),
1277
- /* @__PURE__ */ jsx(VisuallyHidden, { id: rowNameId, children: rowName(columns, row, index, context) }),
1278
- rowActions ? /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__row-actions", children: /* @__PURE__ */ jsx(
1279
- Menu,
1257
+ /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__header-label", children: column.label }),
1258
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: `, ${labels.types[column.type]}` }),
1259
+ sorted ? /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__sort", "aria-hidden": "true", children: sorted === "ascending" ? /* @__PURE__ */ jsx(ArrowUpGlyph, { size: "xs" }) : /* @__PURE__ */ jsx(ArrowDownGlyph, { size: "xs" }) }) : null,
1260
+ here.length > 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__header-presence", "aria-hidden": "true", children: here.map((entry) => /* @__PURE__ */ jsx(Avatar, { name: entry.name, kind: entry.kind, size: 16, decorative: true, className: cx("crewlet-sheet-grid__header-badge", presenceHueClass(entry.hue)) }, entry.id)) }) : null
1261
+ ]
1262
+ },
1263
+ column.field
1264
+ )
1265
+ );
1266
+ return columnSettings ? /* @__PURE__ */ jsx(
1267
+ Popover,
1268
+ {
1269
+ label: labels.columnSettings(column.label),
1270
+ open: openColumn === column.field,
1271
+ onOpenChange: (open) => {
1272
+ if (!open && openColumn === column.field) setOpenColumn(null);
1273
+ },
1274
+ trigger: () => header,
1275
+ className: "crewlet-sheet-grid__column-settings",
1276
+ children: (close) => renderColumnEditor(column, close)
1277
+ },
1278
+ column.field
1279
+ ) : header;
1280
+ })
1281
+ ] }) }),
1282
+ /* @__PURE__ */ jsx("div", { role: "rowgroup", className: "crewlet-sheet-grid__body", style: { height: `${rows.length * rowHeight}px` }, children: drawn.map((index) => {
1283
+ const row = rows[index];
1284
+ const rowPresence = onRows.get(row.id);
1285
+ const rowNameId = `${id}-r${index}-name`;
1286
+ const rowSelected = range !== null && index >= range.top && index <= range.bottom;
1287
+ const actionsLabel = labels.rowActions(index + 1);
1288
+ return /* @__PURE__ */ jsxs(
1289
+ "div",
1290
+ {
1291
+ role: "row",
1292
+ "aria-rowindex": index + 2,
1293
+ className: "crewlet-sheet-grid__row",
1294
+ style: { top: `${index * rowHeight}px` },
1295
+ "data-row-id": row.id,
1296
+ children: [
1297
+ /* @__PURE__ */ jsxs(
1298
+ "div",
1299
+ {
1300
+ role: "rowheader",
1301
+ "aria-colindex": 1,
1302
+ "aria-labelledby": rowNameId,
1303
+ className: cx(
1304
+ "crewlet-sheet-grid__rownum",
1305
+ lastFrozen < 0 && "crewlet-sheet-grid__frozen-edge",
1306
+ rowActions && "crewlet-sheet-grid__rownum--actions",
1307
+ rowSelected && "crewlet-sheet-grid__rownum--selected",
1308
+ rowMenu === row.id && "crewlet-sheet-grid__rownum--open",
1309
+ rowPresence && "crewlet-sheet-grid__rownum--present",
1310
+ rowPresence && presenceHueClass(rowPresence.hue)
1311
+ ),
1312
+ children: [
1313
+ /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__rownum-number", "aria-hidden": "true", children: index + 1 }),
1314
+ /* @__PURE__ */ jsx(VisuallyHidden, { id: rowNameId, children: rowName(columns, row, index, context) }),
1315
+ rowActions ? /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__row-actions", children: /* @__PURE__ */ jsx(
1316
+ Menu,
1317
+ {
1318
+ label: actionsLabel,
1319
+ triggerTabIndex: -1,
1320
+ open: rowMenu === row.id,
1321
+ onOpenChange: (open) => setRowMenu(open ? row.id : null),
1322
+ renderTrigger: (trigger) => (
1323
+ // Lined up with the button's leading edge: the gutter is the
1324
+ // grid's first column, and a centred tip hung past the grid.
1325
+ /* @__PURE__ */ jsx(Tooltip, { content: actionsLabel, align: "start", children: /* @__PURE__ */ jsx(IconButton, { ...trigger, label: actionsLabel, icon: /* @__PURE__ */ jsx(EllipsisGlyph, {}), size: "sm", title: "" }) })
1326
+ ),
1327
+ items: [
1328
+ {
1329
+ key: "delete",
1330
+ label: labels.deleteRow,
1331
+ icon: /* @__PURE__ */ jsx(TrashGlyph, { size: "sm" }),
1332
+ danger: true,
1333
+ onSelect: () => onDeleteRow?.(row.id)
1334
+ }
1335
+ ]
1336
+ }
1337
+ ) }) : null
1338
+ ]
1339
+ }
1340
+ ),
1341
+ columns.map((column, col) => {
1342
+ const position = { row: index, col };
1343
+ const key = cellKey(row.id, column.field);
1344
+ const isActive = current?.row === index && current.col === col;
1345
+ const selected = range ? inRange(range, index, col) : false;
1346
+ const pending = row.pending?.includes(column.field) ?? false;
1347
+ const refused = row.refused?.[column.field] ?? local.get(key);
1348
+ const change = row.changed?.[column.field];
1349
+ const value = normalizeValue(column, row.values[column.field]);
1350
+ const people2 = onCells.get(key);
1351
+ const lead = people2?.[0];
1352
+ const isEditing = editing?.rowId === row.id && editing.field === column.field;
1353
+ const history = isActive && onShowHistory !== void 0 && !isEditing;
1354
+ return (
1355
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events -- keys reach the grid's own handler, which toggles on Space and Enter
1356
+ /* @__PURE__ */ jsxs(
1357
+ "div",
1280
1358
  {
1281
- label: actionsLabel,
1282
- triggerTabIndex: -1,
1283
- open: rowMenu === row.id,
1284
- onOpenChange: (open) => setRowMenu(open ? row.id : null),
1285
- renderTrigger: (trigger) => (
1286
- // Lined up with the button's leading edge: the gutter is the
1287
- // grid's first column, and a centred tip hung past the grid.
1288
- /* @__PURE__ */ jsx(Tooltip, { content: actionsLabel, align: "start", children: /* @__PURE__ */ jsx(IconButton, { ...trigger, label: actionsLabel, icon: /* @__PURE__ */ jsx(EllipsisGlyph, {}), size: "sm", title: "" }) })
1359
+ id: cellDomId(id, position),
1360
+ role: "gridcell",
1361
+ "aria-colindex": col + 2,
1362
+ "aria-selected": selected,
1363
+ "aria-readonly": !cellEditable(column) || void 0,
1364
+ "aria-describedby": isActive ? describedId : void 0,
1365
+ tabIndex: isActive && !isEditing ? 0 : -1,
1366
+ className: cx(
1367
+ "crewlet-sheet-grid__cell",
1368
+ `crewlet-sheet-grid__cell--${column.type}`,
1369
+ column.frozen && "crewlet-sheet-grid__cell--frozen",
1370
+ col === lastFrozen && "crewlet-sheet-grid__frozen-edge",
1371
+ selected && "crewlet-sheet-grid__cell--selected",
1372
+ isActive && "crewlet-sheet-grid__cell--active",
1373
+ pending && "crewlet-sheet-grid__cell--pending",
1374
+ refused !== void 0 && "crewlet-sheet-grid__cell--refused",
1375
+ isEditing && "crewlet-sheet-grid__cell--editing",
1376
+ history && "crewlet-sheet-grid__cell--history",
1377
+ lead && "crewlet-presence-ring",
1378
+ lead && presenceHueClass(lead.hue),
1379
+ lead && (lead.idle ? "crewlet-presence-ring--idle" : null)
1289
1380
  ),
1290
- items: [
1291
- {
1292
- key: "delete",
1293
- label: labels.deleteRow,
1294
- icon: /* @__PURE__ */ jsx(TrashGlyph, { size: "sm" }),
1295
- danger: true,
1296
- onSelect: () => onDeleteRow?.(row.id)
1381
+ style: column.frozen ? { insetInlineStart: `${offsets[col]}px` } : void 0,
1382
+ onMouseDown: (event) => onCellMouseDown(event, { rowId: row.id, field: column.field }),
1383
+ onFocus: (event) => {
1384
+ if (event.target === event.currentTarget) onCellFocus({ rowId: row.id, field: column.field });
1385
+ },
1386
+ onMouseEnter: () => onCellMouseEnter({ rowId: row.id, field: column.field }),
1387
+ onDoubleClick: () => {
1388
+ if (column.type !== "checkbox") beginEdit({ rowId: row.id, field: column.field });
1389
+ },
1390
+ onClick: (event) => {
1391
+ if (column.type === "checkbox" && event.target.closest(".crewlet-sheet-cell__check")) {
1392
+ toggle({ rowId: row.id, field: column.field });
1297
1393
  }
1394
+ },
1395
+ children: [
1396
+ /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__value", children: /* @__PURE__ */ jsx(CellDisplay, { column, value, context, onOpenLink }) }),
1397
+ refused !== void 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__refused", "aria-hidden": "true", children: /* @__PURE__ */ jsx(CircleAlertGlyph, { size: "xs" }) }) : null,
1398
+ change ? /* @__PURE__ */ jsx(
1399
+ ChangeMark,
1400
+ {
1401
+ by: change.by,
1402
+ at: change.at,
1403
+ previous: hasPrevious(change) ? displayText(column, normalizeValue(column, change.previous), context) || null : null,
1404
+ locale,
1405
+ labels
1406
+ }
1407
+ ) : null,
1408
+ people2 && lead ? /* @__PURE__ */ jsx(CursorFlag, { name: flagText(people2), hue: lead.hue, kind: lead.kind, editing: people2.some((entry) => entry.editing) }) : null,
1409
+ isActive && refused !== void 0 && !isEditing ? /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__message", "aria-hidden": "true", children: refused }) : null,
1410
+ history ? /* @__PURE__ */ jsx(
1411
+ "button",
1412
+ {
1413
+ type: "button",
1414
+ tabIndex: -1,
1415
+ "aria-hidden": "true",
1416
+ className: "crewlet-sheet-grid__history",
1417
+ title: labels.history,
1418
+ onMouseDown: (event) => event.stopPropagation(),
1419
+ onClick: () => onShowHistory?.({ rowId: row.id, field: column.field }),
1420
+ children: /* @__PURE__ */ jsx(RotateCcwClockGlyph, { size: "xs" })
1421
+ }
1422
+ ) : null,
1423
+ isEditing ? /* @__PURE__ */ jsx(
1424
+ CellEditor,
1425
+ {
1426
+ column,
1427
+ value,
1428
+ context,
1429
+ seed: editing.seed,
1430
+ label: `${column.label}, ${rowName(columns, row, index, context)}`,
1431
+ side: side(index),
1432
+ conflict,
1433
+ onCommit: (next, move) => endEdit(next, move),
1434
+ onCancel: () => endEdit(void 0, null)
1435
+ }
1436
+ ) : null
1298
1437
  ]
1299
- }
1300
- ) }) : null
1301
- ]
1302
- }
1303
- ),
1304
- columns.map((column, col) => {
1305
- const position = { row: index, col };
1306
- const key = cellKey(row.id, column.field);
1307
- const isActive = current?.row === index && current.col === col;
1308
- const selected = range ? inRange(range, index, col) : false;
1309
- const pending = row.pending?.includes(column.field) ?? false;
1310
- const refused = row.refused?.[column.field] ?? local.get(key);
1311
- const change = row.changed?.[column.field];
1312
- const value = normalizeValue(column, row.values[column.field]);
1313
- const people2 = onCells.get(key);
1314
- const lead = people2?.[0];
1315
- const isEditing = editing?.rowId === row.id && editing.field === column.field;
1316
- const history = isActive && onShowHistory !== void 0 && !isEditing;
1317
- return (
1318
- // eslint-disable-next-line jsx-a11y/click-events-have-key-events -- keys reach the grid's own handler, which toggles on Space and Enter
1319
- /* @__PURE__ */ jsxs(
1320
- "div",
1321
- {
1322
- id: cellDomId(id, position),
1323
- role: "gridcell",
1324
- "aria-colindex": col + 2,
1325
- "aria-selected": selected,
1326
- "aria-readonly": !cellEditable(column) || void 0,
1327
- "aria-describedby": isActive ? describedId : void 0,
1328
- tabIndex: isActive && !isEditing ? 0 : -1,
1329
- className: cx(
1330
- "crewlet-sheet-grid__cell",
1331
- `crewlet-sheet-grid__cell--${column.type}`,
1332
- column.frozen && "crewlet-sheet-grid__cell--frozen",
1333
- col === lastFrozen && "crewlet-sheet-grid__frozen-edge",
1334
- selected && "crewlet-sheet-grid__cell--selected",
1335
- isActive && "crewlet-sheet-grid__cell--active",
1336
- pending && "crewlet-sheet-grid__cell--pending",
1337
- refused !== void 0 && "crewlet-sheet-grid__cell--refused",
1338
- isEditing && "crewlet-sheet-grid__cell--editing",
1339
- history && "crewlet-sheet-grid__cell--history",
1340
- lead && "crewlet-presence-ring",
1341
- lead && presenceHueClass(lead.hue),
1342
- lead && (lead.idle ? "crewlet-presence-ring--idle" : null)
1343
- ),
1344
- style: column.frozen ? { insetInlineStart: `${offsets[col]}px` } : void 0,
1345
- onMouseDown: (event) => onCellMouseDown(event, { rowId: row.id, field: column.field }),
1346
- onFocus: (event) => {
1347
- if (event.target === event.currentTarget) onCellFocus({ rowId: row.id, field: column.field });
1348
- },
1349
- onMouseEnter: () => onCellMouseEnter({ rowId: row.id, field: column.field }),
1350
- onDoubleClick: () => {
1351
- if (column.type !== "checkbox") beginEdit({ rowId: row.id, field: column.field });
1352
- },
1353
- onClick: (event) => {
1354
- if (column.type === "checkbox" && event.target.closest(".crewlet-sheet-cell__check")) {
1355
- toggle({ rowId: row.id, field: column.field });
1356
- }
1357
1438
  },
1358
- children: [
1359
- /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__value", children: /* @__PURE__ */ jsx(CellDisplay, { column, value, context, onOpenLink }) }),
1360
- refused !== void 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__refused", "aria-hidden": "true", children: /* @__PURE__ */ jsx(CircleAlertGlyph, { size: "xs" }) }) : null,
1361
- change ? /* @__PURE__ */ jsx(
1362
- ChangeMark,
1363
- {
1364
- by: change.by,
1365
- at: change.at,
1366
- previous: hasPrevious(change) ? displayText(column, normalizeValue(column, change.previous), context) || null : null,
1367
- locale,
1368
- labels
1369
- }
1370
- ) : null,
1371
- people2 && lead ? /* @__PURE__ */ jsx(CursorFlag, { name: flagText(people2), hue: lead.hue, kind: lead.kind, editing: people2.some((entry) => entry.editing) }) : null,
1372
- isActive && refused !== void 0 && !isEditing ? /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__message", "aria-hidden": "true", children: refused }) : null,
1373
- history ? /* @__PURE__ */ jsx(
1374
- "button",
1375
- {
1376
- type: "button",
1377
- tabIndex: -1,
1378
- "aria-hidden": "true",
1379
- className: "crewlet-sheet-grid__history",
1380
- title: labels.history,
1381
- onMouseDown: (event) => event.stopPropagation(),
1382
- onClick: () => onShowHistory?.({ rowId: row.id, field: column.field }),
1383
- children: /* @__PURE__ */ jsx(RotateCcwClockGlyph, { size: "xs" })
1384
- }
1385
- ) : null,
1386
- isEditing ? /* @__PURE__ */ jsx(
1387
- CellEditor,
1388
- {
1389
- column,
1390
- value,
1391
- context,
1392
- seed: editing.seed,
1393
- label: `${column.label}, ${rowName(columns, row, index, context)}`,
1394
- side: side(index),
1395
- conflict,
1396
- onCommit: (next, move) => endEdit(next, move),
1397
- onCancel: () => endEdit(void 0, null)
1398
- }
1399
- ) : null
1400
- ]
1401
- },
1402
- column.field
1403
- )
1404
- );
1405
- })
1406
- ]
1407
- },
1408
- row.id
1409
- );
1410
- }) })
1411
- ]
1412
- }
1413
- )
1439
+ column.field
1440
+ )
1441
+ );
1442
+ })
1443
+ ]
1444
+ },
1445
+ row.id
1446
+ );
1447
+ }) })
1448
+ ]
1449
+ }
1450
+ ),
1451
+ addsColumns ? (
1452
+ // OUTSIDE THE GRID'S ROLES: a grid's rows hold cells only, so the
1453
+ // control stands beside the header row, sticks with it, and is its
1454
+ // own tab stop after the grid's.
1455
+ /* @__PURE__ */ jsx("div", { className: "crewlet-sheet-grid__add-column", style: { height: `${headerHeight}px` }, children: /* @__PURE__ */ jsx(Tooltip, { content: labels.addColumn, children: /* @__PURE__ */ jsx(IconButton, { label: labels.addColumn, icon: /* @__PURE__ */ jsx(PlusGlyph, {}), size: "sm", title: "", onClick: onAddColumn }) }) })
1456
+ ) : null
1457
+ ]
1414
1458
  }
1415
1459
  ),
1416
1460
  samples ? /* @__PURE__ */ jsx("div", { ref: measurer, className: "crewlet-sheet-grid__measure", "aria-hidden": "true", inert: true, children: ordered.filter((column) => samples.has(column.field)).map((column) => /* @__PURE__ */ jsxs("div", { "data-measure": column.field, children: [
@@ -1421,7 +1465,7 @@ function SheetGrid({
1421
1465
  (samples.get(column.field) ?? []).map((value, index) => /* @__PURE__ */ jsx("div", { className: cx("crewlet-sheet-grid__cell", `crewlet-sheet-grid__cell--${column.type}`), children: /* @__PURE__ */ jsx("span", { className: "crewlet-sheet-grid__value", children: /* @__PURE__ */ jsx(CellDisplay, { column, value, context }) }) }, index))
1422
1466
  ] }, column.field)) }) : null,
1423
1467
  /* @__PURE__ */ jsx(VisuallyHidden, { id: describedId, children: description }),
1424
- /* @__PURE__ */ jsx(VisuallyHidden, { id: helpId, children: rowActions ? `${labels.keyboardHelp} ${labels.rowActionsHelp}` : labels.keyboardHelp }),
1468
+ /* @__PURE__ */ jsx(VisuallyHidden, { id: helpId, children: [labels.keyboardHelp, rowActions ? labels.rowActionsHelp : "", columnSettings ? labels.columnSettingsHelp : ""].filter(Boolean).join(" ") }),
1425
1469
  /* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx("span", { role: "status", "aria-live": "polite", children: /* @__PURE__ */ jsx("span", { children: announcement.text }, announcement.n) }) }),
1426
1470
  onAddRow || sync ? /* @__PURE__ */ jsxs("div", { className: "crewlet-sheet-grid__foot", children: [
1427
1471
  onAddRow && editable ? /* @__PURE__ */ jsx(Button, { size: "small", variant: "ghost", onClick: onAddRow, leadingIcon: /* @__PURE__ */ jsx(PlusGlyph, { size: "sm" }), children: labels.addRow }) : null,
@@ -1442,6 +1486,9 @@ function SheetGrid({
1442
1486
  ] }) : null
1443
1487
  ] });
1444
1488
  }
1489
+ function fromElsewhere(event) {
1490
+ return !event.currentTarget.contains(event.target);
1491
+ }
1445
1492
  function cellDomId(grid, position) {
1446
1493
  return `${grid}-r${position.row < 0 ? "h" : position.row}-c${position.col}`;
1447
1494
  }