@crisp-ui-kit/crisp 0.54.10 → 0.55.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 (77) hide show
  1. package/dist/cjs/components/datatable/datatable.js +249 -46
  2. package/dist/cjs/components/datatable/datatable.recipe.js +24 -0
  3. package/dist/cjs/components/resizablepanel/resizablepanel.js +10 -1
  4. package/dist/cjs/components/table/table.js +8 -1
  5. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +25 -2
  6. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +176 -43
  7. package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +28 -9
  8. package/dist/cjs/index.js +0 -2
  9. package/dist/cjs/product/recordpage/column.js +19 -0
  10. package/dist/cjs/product/recordpage/index.js +6 -1
  11. package/dist/cjs/product/recordpage/recordpage.js +25 -8
  12. package/dist/cjs/product/recordpage/test-fixtures.js +55 -0
  13. package/dist/cjs/product/savedviews/savedviews.recipe.js +13 -11
  14. package/dist/datatable.css +14 -0
  15. package/dist/datatable.layered.css +14 -0
  16. package/dist/esm/components/datatable/datatable.js +250 -47
  17. package/dist/esm/components/datatable/datatable.recipe.js +24 -0
  18. package/dist/esm/components/resizablepanel/resizablepanel.js +10 -1
  19. package/dist/esm/components/table/table.js +8 -1
  20. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +25 -2
  21. package/dist/esm/components/tabletoolbar/tabletoolbar.js +177 -44
  22. package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +28 -9
  23. package/dist/esm/index.js +0 -2
  24. package/dist/esm/product/recordpage/column.js +16 -0
  25. package/dist/esm/product/recordpage/index.js +2 -0
  26. package/dist/esm/product/recordpage/recordpage.js +22 -5
  27. package/dist/esm/product/recordpage/test-fixtures.js +52 -0
  28. package/dist/esm/product/savedviews/savedviews.recipe.js +13 -11
  29. package/dist/savedviews.css +6 -18
  30. package/dist/savedviews.layered.css +6 -18
  31. package/dist/styles.css +43 -276
  32. package/dist/styles.layered.css +43 -276
  33. package/dist/tabletoolbar.css +23 -3
  34. package/dist/tabletoolbar.layered.css +23 -3
  35. package/dist/types/components/datatable/datatable.d.ts +30 -3
  36. package/dist/types/components/table/table.d.ts +14 -0
  37. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +26 -1
  38. package/dist/types/index.d.ts +0 -2
  39. package/dist/types/product/recordpage/column.d.ts +112 -0
  40. package/dist/types/product/recordpage/index.d.ts +2 -0
  41. package/dist/types/product/recordpage/recordpage.d.ts +1 -1
  42. package/dist/types/product/recordpage/test-fixtures.d.ts +3 -0
  43. package/dist/types/product/recordpanel/recordpanel.d.ts +1 -1
  44. package/package.json +2 -12
  45. package/dist/cjs/product/recordstable/company-logos.js +0 -29
  46. package/dist/cjs/product/recordstable/index.js +0 -13
  47. package/dist/cjs/product/recordstable/recordstable.js +0 -1357
  48. package/dist/cjs/product/recordstable/recordstable.recipe.js +0 -175
  49. package/dist/cjs/product/recordstable/recordstable.tag.js +0 -26
  50. package/dist/cjs/product/taskstable/index.js +0 -7
  51. package/dist/cjs/product/taskstable/taskstable.js +0 -27
  52. package/dist/cjs/product/taskstable/taskstable.recipe.js +0 -56
  53. package/dist/esm/product/recordstable/company-logos.js +0 -26
  54. package/dist/esm/product/recordstable/index.js +0 -3
  55. package/dist/esm/product/recordstable/recordstable.js +0 -1317
  56. package/dist/esm/product/recordstable/recordstable.recipe.js +0 -172
  57. package/dist/esm/product/recordstable/recordstable.tag.js +0 -23
  58. package/dist/esm/product/taskstable/index.js +0 -2
  59. package/dist/esm/product/taskstable/taskstable.js +0 -24
  60. package/dist/esm/product/taskstable/taskstable.recipe.js +0 -53
  61. package/dist/recordstable.css +0 -223
  62. package/dist/recordstable.d.ts +0 -7
  63. package/dist/recordstable.layered.css +0 -225
  64. package/dist/taskstable.css +0 -30
  65. package/dist/taskstable.d.ts +0 -7
  66. package/dist/taskstable.layered.css +0 -32
  67. package/dist/types/product/recordstable/company-logos.d.ts +0 -1
  68. package/dist/types/product/recordstable/index.d.ts +0 -3
  69. package/dist/types/product/recordstable/recordstable.d.ts +0 -289
  70. package/dist/types/product/recordstable/recordstable.recipe.d.ts +0 -5
  71. package/dist/types/product/recordstable/recordstable.tag.d.ts +0 -15
  72. package/dist/types/product/taskstable/index.d.ts +0 -2
  73. package/dist/types/product/taskstable/taskstable.d.ts +0 -54
  74. package/dist/types/product/taskstable/taskstable.recipe.d.ts +0 -3
  75. /package/dist/cjs/product/{recordstable → recordpage}/editable-field.js +0 -0
  76. /package/dist/esm/product/{recordstable → recordpage}/editable-field.js +0 -0
  77. /package/dist/types/product/{recordstable → recordpage}/editable-field.d.ts +0 -0
@@ -1,225 +0,0 @@
1
- @layer crisp-components {
2
- .crisp-recordstable {
3
- box-sizing: border-box; position: relative; display: flex; flex-direction: column; min-height: 0; height: 100%; width: 100%; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
4
- & * { box-sizing: border-box; }
5
- /* toolbar */
6
- & .crisp-recordstable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); padding: 0 var(--crisp-space-3) var(--crisp-space-2); }
7
- & .crisp-recordstable-toolbar[data-inline] { padding-bottom: 0; }
8
- & .crisp-recordstable-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); color: var(--crisp-fg-muted); }
9
- & .crisp-recordstable-search svg { flex-shrink: 0; }
10
- & .crisp-recordstable-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); width: 180px; }
11
- & .crisp-recordstable-search input::placeholder { color: var(--crisp-fg-muted); }
12
- & .crisp-recordstable-tool { display: inline-flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; box-shadow: inset 0 0 0 1px var(--crisp-border-default); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s; }
13
- & .crisp-recordstable-tool:hover { background: var(--crisp-bg-raised); }
14
- & .crisp-recordstable-tool svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
15
- & .crisp-recordstable-tool--active { background: var(--crisp-bg-raised); }
16
- & .crisp-recordstable-tool--ghost { box-shadow: none; background: color-mix(in srgb, var(--crisp-fg-primary) 3%, transparent); color: var(--crisp-fg-muted); }
17
- & .crisp-recordstable-tool--ghost:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); }
18
- & .crisp-recordstable-toolgroup { position: relative; display: inline-flex; }
19
- & .crisp-recordstable-divider { flex-shrink: 0; width: 1px; height: 20px; background: var(--crisp-border-subtle); }
20
- & .crisp-recordstable-menu { position: absolute; top: calc(100% + var(--crisp-space-1)); left: 0; z-index: 30; min-width: 232px; padding: var(--crisp-space-1); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover); box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3); display: flex; flex-direction: column; gap: var(--crisp-space-px); transform-origin: top; opacity: 1; transform: none; transition: opacity .13s ease, transform .13s ease; }
21
- & .crisp-recordstable-menu { @starting-style { opacity: 0; transform: scale(0.96); } }
22
- & .crisp-recordstable-ctx { z-index: 60; }
23
- & .crisp-recordstable-th[data-drop] { box-shadow: inset 2px 0 0 var(--crisp-bg-brand-solid); }
24
- & .crisp-recordstable-th-inner[draggable="true"]:active { cursor: grabbing; }
25
- & .crisp-recordstable-menu-head { padding: var(--crisp-space-1) var(--crisp-space-2) var(--crisp-space-0-5); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
26
- & .crisp-recordstable-sortpop { min-width: 480px; overflow: visible; padding: var(--crisp-space-3) var(--crisp-space-1-5); gap: var(--crisp-space-2); }
27
- & .crisp-recordstable-sort-empty { padding: var(--crisp-space-1) var(--crisp-space-1) var(--crisp-space-2); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
28
- & .crisp-recordstable-sort-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); }
29
- & .crisp-recordstable-sort-grip { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 28px; flex-shrink: 0; margin-right: calc(-1 * var(--crisp-space-1)); padding: 0; border: none; background: none; color: var(--crisp-fg-muted); cursor: grab; }
30
- & .crisp-recordstable-sort-grip:active { cursor: grabbing; }
31
- & .crisp-recordstable-sort-grip:hover { color: var(--crisp-fg-primary); }
32
- & .crisp-recordstable-sort-optlabel { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
33
- & .crisp-recordstable-sort-optlabel svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
34
- & .crisp-recordstable-sort-field { flex: 1; min-width: 0; }
35
- & .crisp-recordstable-sort-dir { width: 136px; flex-shrink: 0; }
36
- & .crisp-recordstable-sort-field .crisp-select-trigger, & .crisp-recordstable-sort-dir .crisp-select-trigger { height: 28px; padding-right: var(--crisp-space-1); }
37
- & .crisp-recordstable-sort-remove { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0; margin: 0; border: none; border-radius: var(--crisp-radius-md); background: none; color: var(--crisp-fg-muted); cursor: pointer; transition: background-color .12s, color .12s; }
38
- & .crisp-recordstable-sort-remove:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
39
- & .crisp-recordstable-sort-addrow { display: flex; padding: var(--crisp-space-0-5) 0; }
40
- & .crisp-recordstable-sort-add { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: 28px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-1-5); margin: 0; border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-primary); cursor: pointer; transition: background-color .12s; }
41
- & .crisp-recordstable-sort-add:hover { background: var(--crisp-bg-raised); }
42
- & .crisp-recordstable-sort-add svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
43
- & .crisp-recordstable-sort-foot { display: flex; padding: var(--crisp-space-1-5) var(--crisp-space-1) var(--crisp-space-0-5); }
44
- & .crisp-recordstable-sort-learn { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); font-size: var(--crisp-text-lg); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); text-decoration: none; }
45
- & .crisp-recordstable-sort-learn svg { flex-shrink: 0; }
46
- & .crisp-recordstable-sort-learn:hover { color: var(--crisp-fg-primary); }
47
- & .crisp-recordstable-menu-item { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; height: var(--crisp-size-menuRow); margin: 0; padding: 0 var(--crisp-space-2); border: none; background: none; border-radius: var(--crisp-radius-md); font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); text-align: left; text-decoration: none; cursor: pointer; }
48
- & .crisp-recordstable-menu-item:hover { background: var(--crisp-bg-raised); }
49
- & .crisp-recordstable-menu-item:disabled { opacity: 0.4; cursor: default; }
50
- & .crisp-recordstable-menu-item:disabled:hover { background: none; }
51
- & .crisp-recordstable-menu-item svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
52
- & .crisp-recordstable-menu-item[data-on] svg { color: var(--crisp-bg-brand-solid); }
53
- & .crisp-recordstable-menu-item .crisp-recordstable-menu-check { margin-left: auto; }
54
- & .crisp-recordstable-menu-input { height: var(--crisp-size-row); margin: var(--crisp-space-0-5); padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); background: var(--crisp-bg-surface); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; }
55
- & .crisp-recordstable-menu-sep { height: 1px; border: none; background: var(--crisp-border-subtle); margin: var(--crisp-space-0-5) 0; }
56
- /* #592: the Filter field picker's nested-scope header (back control +
57
- search, once drilled into a nestedColumns column) and a search
58
- result's own breadcrumb row ("Team › Twitter follower count", surfaced by the SAME query matching a nested column). menu-back reuses the established 28px icon-only control pattern this file already uses twice (sort-remove, the kebab segment) rather than an independent measurement of Attio's own back arrow. The breadcrumb
59
- colours ARE measured live: the parent segment muted at the same
60
- exact color-mix 63% sort-learn above already uses, the
61
- separator chevron and leaf segment both full fgPrimary (the
62
- generic menu-item svg rule below defaults every icon to
63
- fgMuted, so the separator needs its own override to read
64
- correctly here). */
65
- & .crisp-recordstable-menu-nesthead { display: flex; align-items: center; gap: var(--crisp-space-1); margin: var(--crisp-space-0-5); }
66
- & .crisp-recordstable-menu-nesthead .crisp-recordstable-menu-input { flex: 1; margin: 0; }
67
- & .crisp-recordstable-menu-back { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: none; color: var(--crisp-fg-muted); cursor: pointer; }
68
- & .crisp-recordstable-menu-back:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
69
- & .crisp-recordstable-menu-item--expand { justify-content: space-between; }
70
- & .crisp-recordstable-menu-item-path { display: inline-flex; align-items: center; gap: 2px; min-width: 0; overflow: hidden; }
71
- & .crisp-recordstable-menu-item-path-parent { flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; max-width: 100px; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
72
- & .crisp-recordstable-menu-item-path-sep { flex-shrink: 0; color: var(--crisp-fg-primary); }
73
- & .crisp-recordstable-menu-item-path > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
74
- & .crisp-recordstable-menu--up { top: auto; bottom: calc(100% + var(--crisp-space-1)); transform-origin: bottom; }
75
- & .crisp-recordstable-menu--right { left: auto; right: 0; }
76
- & .crisp-recordstable-menu--fixed { position: fixed; top: auto; left: auto; z-index: 40; }
77
- & .crisp-recordstable-menu--hdr { min-width: 200px; }
78
- & .crisp-recordstable-menu--op { min-width: 0; }
79
- & .crisp-recordstable-menu-item--danger { color: var(--crisp-bg-danger); }
80
- & .crisp-recordstable-menu-item--danger svg { color: var(--crisp-bg-danger); }
81
- & .crisp-recordstable-menu-item--danger:hover { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
82
- & .crisp-recordstable-menu-check { margin-left: auto; color: var(--crisp-bg-brand-solid); }
83
- & .crisp-recordstable-fchip { display: inline-flex; align-items: center; height: var(--crisp-size-row); padding-right: var(--crisp-space-1); border-radius: var(--crisp-radius-md); box-shadow: inset 0 0 0 1px var(--crisp-border-default); background: var(--crisp-bg-surface); font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); white-space: nowrap; }
84
- & .crisp-recordstable-fchip-main { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: 100%; margin: 0; padding: 0 var(--crisp-space-1) 0 var(--crisp-space-2); border: none; background: none; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); cursor: pointer; }
85
- & .crisp-recordstable-fchip-field { font-weight: var(--crisp-weight-medium); }
86
- & .crisp-recordstable-fchip-op { color: var(--crisp-fg-muted); }
87
- & .crisp-recordstable-fchip-val { font-weight: var(--crisp-weight-medium); max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
88
- & .crisp-recordstable-fchip-x { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; color: var(--crisp-fg-muted); cursor: pointer; }
89
- & .crisp-recordstable-fchip-x:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 10%, transparent); color: var(--crisp-fg-primary); }
90
- & .crisp-recordstable-spacer { flex: 1; }
91
- & .crisp-recordstable-count { font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
92
- & .crisp-recordstable-new { display: inline-flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); cursor: pointer; transition: background-color .12s; }
93
- & .crisp-recordstable-new:hover { background: var(--crisp-bg-brand-solid-pressed); }
94
-
95
- /* scroll frame + table */
96
- & .crisp-recordstable-scroll { flex: 1; min-height: 0; overflow: auto; background: var(--crisp-bg-surface); border-top: 1px solid var(--crisp-border-subtle); scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--crisp-fg-primary) 22%, transparent) transparent; }
97
- & .crisp-recordstable-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
98
- & .crisp-recordstable-scroll::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--crisp-fg-primary) 20%, transparent); border-radius: var(--crisp-radius-full); border: 2px solid var(--crisp-bg-surface); background-clip: padding-box; }
99
- & .crisp-recordstable-scroll::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 34%, transparent); background-clip: padding-box; }
100
- & .crisp-recordstable-scroll::-webkit-scrollbar-corner { background: transparent; }
101
- & .crisp-recordstable-table { table-layout: fixed; border-collapse: separate; border-spacing: 0; }
102
- & .crisp-recordstable-th, & .crisp-recordstable-td { position: relative; height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); border-bottom: 1px solid var(--crisp-border-subtle); border-inline-end: 1px solid var(--crisp-border-subtle); text-align: start; white-space: nowrap; overflow: hidden; }
103
- & .crisp-recordstable-th { position: sticky; top: 0; z-index: 2; padding: 0; height: var(--crisp-size-tableHeader); background: var(--crisp-bg-surface); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); user-select: none; }
104
- & .crisp-recordstable-th-inner { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; height: 100%; margin: 0; padding: 0 var(--crisp-space-2); border: none; background: none; color: inherit; font: inherit; letter-spacing: inherit; text-align: start; cursor: pointer; }
105
- & .crisp-recordstable-th-inner svg { flex-shrink: 0; color: color-mix(in srgb, var(--crisp-fg-primary) 45%, transparent); }
106
- & .crisp-recordstable-th-label { flex: 1; overflow: hidden; text-overflow: ellipsis; }
107
- & .crisp-recordstable-th-rename { width: 100%; min-width: 0; height: 100%; margin: 0; padding: 0; border: none; outline: none; background: none; color: inherit; font: inherit; letter-spacing: inherit; }
108
- & .crisp-recordstable-th-rename:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
109
- & .crisp-recordstable-sort { flex-shrink: 0; opacity: 0.4; transition: opacity .12s; color: color-mix(in srgb, var(--crisp-fg-primary) 45%, transparent); }
110
- & .crisp-recordstable-th:hover .crisp-recordstable-sort, & .crisp-recordstable-th[data-sorted] .crisp-recordstable-sort { opacity: 1; }
111
- & .crisp-recordstable-th[data-sorted="desc"] .crisp-recordstable-sort svg { transform: rotate(180deg); }
112
-
113
- /* column resize handle */
114
- & .crisp-recordstable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
115
- & .crisp-recordstable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: var(--crisp-bg-brand-solid); opacity: 0; transition: opacity .12s; }
116
- & .crisp-recordstable-resize:hover::after, & .crisp-recordstable-resize[data-resizing]::after { opacity: 1; }
117
-
118
- /* add-column header */
119
- & .crisp-recordstable-addcol { min-width: 44px; }
120
- & .crisp-recordstable-addcol button { display: flex; align-items: center; justify-content: center; width: 44px; height: 100%; border: none; background: none; color: var(--crisp-fg-muted); cursor: pointer; }
121
- & .crisp-recordstable-addcol button:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
122
-
123
- /* cells */
124
- /* font-weight is explicit because the first cell of each row is a
125
- th[scope=row] — the record's name is what the row IS — and a th defaults to bold. Measured before the change and unchanged after: 400. */
126
- & .crisp-recordstable-td { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-primary); background: var(--crisp-bg-surface); cursor: default; }
127
- & .crisp-recordstable-td--editable { cursor: text; }
128
- /* keyboard/click cell selection (spreadsheet-style) */
129
- & .crisp-recordstable-td[data-selected] { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: -2px; border-radius: var(--crisp-radius-sm); z-index: 3; }
130
- & .crisp-recordstable-scroll:focus { outline: none; }
131
- /* picker cells (status / date / reference) host a ghost control edge-to-edge */
132
- & .crisp-recordstable-td--picker { padding: 0; }
133
- & .crisp-recordstable-td--picker .crisp-select-trigger, & .crisp-recordstable-td--picker .crisp-datepicker-trigger, & .crisp-recordstable-td--picker .crisp-multiselect-trigger { width: 100%; height: 100%; min-height: 0; border-radius: 0; box-shadow: none; padding: 0 var(--crisp-space-2); }
134
- & .crisp-recordstable-td--picker .crisp-multiselect-trigger:hover { background: none; }
135
- & .crisp-recordstable-td--picker .crisp-multiselect-trigger[data-open] { box-shadow: inset 0 0 0 1.5px var(--crisp-bg-brand-solid); }
136
- /* tag cell: one compact clipped line of full chips, no chevron (opens the picker on click) */
137
- & .crisp-recordstable-td--picker .crisp-multiselect-chips { flex-wrap: nowrap; overflow: hidden; }
138
- & .crisp-recordstable-td--picker .crisp-multiselect-chips .crisp-recordstable-tag { flex-shrink: 0; max-width: none; }
139
- & .crisp-recordstable-td--picker .crisp-multiselect-chevron { display: none; }
140
- & .crisp-recordstable-td--picker .crisp-multiselect-placeholder { color: var(--crisp-fg-muted); }
141
- /* reference picker: a leading 16px avatar per option, plus a trailing "Create X" row when the consumer supplies one (#449) -- measured live against Attio's own picker (16px circular avatar, 16px plain icon for
142
- the create row, both sitting in the option's existing 8px gap, no new gap value invented). */
143
- & .crisp-recordstable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
144
- & .crisp-recordstable-refoption-avatar { flex-shrink: 0; }
145
- & .crisp-recordstable-refoption--create { color: var(--crisp-fg-muted); }
146
- & .crisp-recordstable-status { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
147
- & .crisp-recordstable-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); }
148
- & .crisp-recordstable-cell { display: flex; align-items: center; gap: var(--crisp-space-2); height: 100%; min-width: 0; }
149
- & .crisp-recordstable-cell--num { justify-content: flex-end; font-variant-numeric: tabular-nums; }
150
- & .crisp-recordstable-cell--tags { gap: var(--crisp-space-1); }
151
- & .crisp-recordstable-tag { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-chip); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); white-space: nowrap; background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 82%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); }
152
- & .crisp-recordstable-tag[data-tone="brand"] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); color: color-mix(in srgb, var(--crisp-bg-brand-solid) 82%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-brand-solid) 30%, transparent); }
153
- & .crisp-recordstable-tag[data-tone="success"] { background: color-mix(in srgb, var(--crisp-bg-success) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-success) 78%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-success) 30%, transparent); }
154
- & .crisp-recordstable-tag[data-tone="warning"] { background: color-mix(in srgb, var(--crisp-bg-warning) 18%, transparent); color: color-mix(in srgb, var(--crisp-bg-warning) 82%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-warning) 30%, transparent); }
155
- & .crisp-recordstable-tag[data-tone="danger"] { background: color-mix(in srgb, var(--crisp-bg-danger) 15%, transparent); color: color-mix(in srgb, var(--crisp-bg-danger) 80%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-danger) 30%, transparent); }
156
- & .crisp-recordstable-tag[data-tone="purple"] { background: color-mix(in srgb, var(--crisp-bg-purple) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-purple) 80%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-purple) 30%, transparent); }
157
- & .crisp-recordstable-tag[data-tone="cyan"] { background: color-mix(in srgb, var(--crisp-bg-cyan) 18%, transparent); color: color-mix(in srgb, var(--crisp-bg-cyan) 82%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-cyan) 30%, transparent); }
158
- & .crisp-recordstable-tag[data-tone="orange"] { background: color-mix(in srgb, var(--crisp-bg-orange) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-orange) 82%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-orange) 30%, transparent); }
159
- & .crisp-recordstable-muted { color: var(--crisp-fg-muted); }
160
- & .crisp-recordstable-entity { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; color: var(--crisp-fg-primary); font-weight: var(--crisp-weight-medium); text-decoration: none; }
161
- & .crisp-recordstable-entity:hover .crisp-recordstable-entity-name { text-decoration: underline; }
162
- & .crisp-recordstable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
163
- & button.crisp-recordstable-entity-name--link { margin: 0; padding: 0; border: none; background: none; font: inherit; color: inherit; letter-spacing: inherit; text-align: start; cursor: pointer; max-width: 100%; }
164
- & .crisp-recordstable-fav { width: var(--crisp-size-icon); height: var(--crisp-size-icon); border-radius: var(--crisp-radius-sm); flex-shrink: 0; object-fit: cover; background: var(--crisp-bg-raised); }
165
- & .crisp-recordstable-open { position: absolute; inset-inline-end: var(--crisp-space-1); top: 50%; transform: translateY(-50%); display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-default); color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; opacity: 0; transition: opacity .12s, background-color .12s; z-index: 3; }
166
- & .crisp-recordstable-open:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
167
- & tbody tr:hover .crisp-recordstable-open { opacity: 1; }
168
- & .crisp-recordstable-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
169
- & .crisp-recordstable-link:hover { text-decoration: underline; }
170
- /* inline edit input */
171
- & .crisp-recordstable-edit { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0 var(--crisp-space-2); border: none; outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: -2px; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); z-index: 5; }
172
- & .crisp-recordstable-edit--num { text-align: end; font-variant-numeric: tabular-nums; }
173
- /* selection / hover / sticky */
174
- & .crisp-recordstable-col-check { width: 40px; min-width: 40px; padding: 0; }
175
- & .crisp-recordstable-check-inner { display: flex; align-items: center; justify-content: center; height: 100%; }
176
- & .crisp-recordstable-col-check, & .crisp-recordstable-col-entity { position: sticky; z-index: 3; background: var(--crisp-bg-surface); }
177
- /* The sticky pin is a LOGICAL offset: with physical `left` these columns were not pinned at all under RTL -- scrolled, they left the viewport entirely (measured: viewport 13..773, check column at x=1333). Nothing in JS measures these, so converting them is safe; the two properties below that have no logical form get an explicit RTL rule rather than being left to point the wrong way. */
178
- & .crisp-recordstable-col-check { inset-inline-start: 0; }
179
- & .crisp-recordstable-col-entity { inset-inline-start: 40px; clip-path: inset(0 -38px 0 0); transition: box-shadow .2s ease; }
180
- [dir="rtl"] & .crisp-recordstable-col-entity { clip-path: inset(0 0 0 -38px); }
181
- & thead .crisp-recordstable-col-check, & thead .crisp-recordstable-col-entity { z-index: 4; }
182
- /* soft shadow on the sticky column edge once the grid is scrolled horizontally */
183
- & .crisp-recordstable-scroll[data-scrolled] .crisp-recordstable-col-entity { box-shadow: 6px 0 16px 4px rgba(0,0,0,0.12); }
184
- /* box-shadow has no logical form either: the bleed follows the scrolled content, which is the opposite side under RTL. */
185
- [dir="rtl"] & .crisp-recordstable-scroll[data-scrolled] .crisp-recordstable-col-entity { box-shadow: -6px 0 16px 4px rgba(0,0,0,0.12); }
186
- /* AI-enriched values carry a purple wash. Measured live: a full-bleed overlay at #9b69ff 4% (== bgPurple), composited OVER whatever row-state background the cell already has, which is why it is an ::after and not a background. */
187
- & .crisp-recordstable-td[data-enriched]::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: color-mix(in srgb, var(--crisp-bg-purple) 4%, transparent); }
188
- & tbody tr:hover .crisp-recordstable-td { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); }
189
- & tbody tr[data-selected] .crisp-recordstable-td { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
190
- & tbody tr[data-selected]:hover .crisp-recordstable-td { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface))); }
191
- & .crisp-recordstable-th:last-child, & .crisp-recordstable-td:last-child { border-inline-end: none; }
192
- /* add-row footer */
193
- & .crisp-recordstable-addrow { display: flex; align-items: center; gap: var(--crisp-space-2); width: 100%; height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); border: none; border-top: 1px solid var(--crisp-border-subtle); background: var(--crisp-bg-surface); color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-lg); cursor: pointer; }
194
- & .crisp-recordstable-addrow:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
195
- /* zero-row empty state — centred in the body region (headers + summary stay) */
196
- & .crisp-recordstable-empty { display: flex; align-items: center; justify-content: center; min-height: 320px; padding: var(--crisp-space-8) var(--crisp-space-4); }
197
- /* summary footer — per-column aggregation, sticky to the bottom */
198
- & .crisp-recordstable-sum { position: sticky; bottom: 0; z-index: 2; height: var(--crisp-size-tableRow); padding: 0; border-top: 1px solid var(--crisp-border-subtle); border-bottom: 1px solid var(--crisp-border-subtle); border-inline-end: 1px solid var(--crisp-border-subtle); background: var(--crisp-bg-surface); }
199
- & tfoot .crisp-recordstable-col-check, & tfoot .crisp-recordstable-col-entity { position: sticky; bottom: 0; z-index: 3; }
200
- & .crisp-recordstable-sum:last-child { border-inline-end: none; }
201
- & .crisp-recordstable-sum-btn { display: flex; align-items: center; justify-content: flex-end; gap: var(--crisp-space-1-5); width: 100%; height: 100%; margin: 0; padding: 0 var(--crisp-space-3); border: none; background: none; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); cursor: pointer; white-space: nowrap; overflow: hidden; }
202
- & .crisp-recordstable-sum-btn:hover { background: var(--crisp-bg-raised); }
203
- & .crisp-recordstable-sum-label { color: color-mix(in srgb, var(--crisp-fg-primary) 45%, transparent); text-transform: capitalize; }
204
- & .crisp-recordstable-sum-value { color: var(--crisp-fg-primary); font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; }
205
- & .crisp-recordstable-sumgroup { width: 100%; height: 100%; }
206
- & .crisp-recordstable-pin { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); color: var(--crisp-bg-warning); fill: color-mix(in srgb, var(--crisp-bg-warning) 35%, transparent); }
207
- & .crisp-recordstable-sum-btn[data-empty] { opacity: 0; justify-content: flex-start; }
208
- & .crisp-recordstable-sum-btn[data-empty]:hover { opacity: 1; }
209
- /* bulk action bar lives in its own component now — see components/bulkbar */
210
- & .crisp-recordstable-bulk-sep { width: 1px; height: 20px; margin: 0 var(--crisp-space-0-5); background: var(--crisp-border-subtle); }
211
- & .crisp-recordstable-bulk-close { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: transparent; color: var(--crisp-fg-muted); cursor: pointer; }
212
- & .crisp-recordstable-bulk-close:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
213
- /* announced, never drawn */
214
- & .crisp-recordstable-live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
215
- [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
216
- }
217
-
218
- @media (prefers-reduced-motion: reduce) {
219
- .crisp-recordstable, .crisp-recordstable * {
220
- animation-duration: 0.01ms !important;
221
- animation-iteration-count: 1 !important;
222
- transition-duration: 0.01ms !important;
223
- }
224
- }
225
- }
@@ -1,30 +0,0 @@
1
- .crisp-taskstable {
2
- display: flex; flex-direction: column; min-height: 0; box-sizing: border-box; font-family: var(--crisp-font-sans); color: var(--crisp-fg-primary); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
3
- & .crisp-taskstable-colhead { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-height: 49px; max-height: 49px; padding: var(--crisp-space-2-5) var(--crisp-space-3); box-shadow: inset 0 -1px 0 var(--crisp-border-subtle); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
4
- & .crisp-taskstable-colhead-cell { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
5
- & .crisp-taskstable-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); }
6
- /* #642: measured live -- title, due date and assignee name are ALL 14px/500 on Attio's own Tasks page. Only -title/-due set their own
7
- font-size (and only -title its own weight), so an unstyled consumer
8
- slot (a plain PersonChip/RecordChip name span with no font-size of
9
- its own) fell through to the browser/body default, 16px/400. Set
10
- the ambient font here so it cascades to any slot content that
11
- doesn't override it -- -title/-due's own declarations still apply,
12
- unchanged, they're just now redundant with this baseline rather than the only source of it. */
13
- & .crisp-taskstable-cell { display: flex; align-items: center; min-width: 0; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); }
14
- & .crisp-taskstable-check { appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-full); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); cursor: pointer; transition: background-color .12s, box-shadow .12s; }
15
- & .crisp-taskstable-check:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 30%, transparent); }
16
- & .crisp-taskstable-check:checked { background: var(--crisp-bg-brand-solid) center / 10px no-repeat; box-shadow: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E"); }
17
- & .crisp-taskstable-check:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
18
- & .crisp-taskstable-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
19
- & .crisp-taskstable-due { font-size: var(--crisp-text-lg); color: var(--crisp-fg-muted); white-space: nowrap; }
20
- & .crisp-taskstable-due[data-overdue] { color: var(--crisp-fg-danger); }
21
- & .crisp-taskstable-empty { padding: var(--crisp-space-8) var(--crisp-space-4); text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
22
- }
23
-
24
- @media (prefers-reduced-motion: reduce) {
25
- .crisp-taskstable, .crisp-taskstable * {
26
- animation-duration: 0.01ms !important;
27
- animation-iteration-count: 1 !important;
28
- transition-duration: 0.01ms !important;
29
- }
30
- }
@@ -1,7 +0,0 @@
1
- import type { RecipeFn } from "../core/types";
2
-
3
- export interface TaskstableVariants {
4
-
5
- }
6
-
7
- export declare const taskstableRecipe: RecipeFn<{ }>;
@@ -1,32 +0,0 @@
1
- @layer crisp-components {
2
- .crisp-taskstable {
3
- display: flex; flex-direction: column; min-height: 0; box-sizing: border-box; font-family: var(--crisp-font-sans); color: var(--crisp-fg-primary); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
4
- & .crisp-taskstable-colhead { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-height: 49px; max-height: 49px; padding: var(--crisp-space-2-5) var(--crisp-space-3); box-shadow: inset 0 -1px 0 var(--crisp-border-subtle); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
5
- & .crisp-taskstable-colhead-cell { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
6
- & .crisp-taskstable-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); }
7
- /* #642: measured live -- title, due date and assignee name are ALL 14px/500 on Attio's own Tasks page. Only -title/-due set their own
8
- font-size (and only -title its own weight), so an unstyled consumer
9
- slot (a plain PersonChip/RecordChip name span with no font-size of
10
- its own) fell through to the browser/body default, 16px/400. Set
11
- the ambient font here so it cascades to any slot content that
12
- doesn't override it -- -title/-due's own declarations still apply,
13
- unchanged, they're just now redundant with this baseline rather than the only source of it. */
14
- & .crisp-taskstable-cell { display: flex; align-items: center; min-width: 0; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); }
15
- & .crisp-taskstable-check { appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-full); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); cursor: pointer; transition: background-color .12s, box-shadow .12s; }
16
- & .crisp-taskstable-check:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 30%, transparent); }
17
- & .crisp-taskstable-check:checked { background: var(--crisp-bg-brand-solid) center / 10px no-repeat; box-shadow: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E"); }
18
- & .crisp-taskstable-check:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
19
- & .crisp-taskstable-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
20
- & .crisp-taskstable-due { font-size: var(--crisp-text-lg); color: var(--crisp-fg-muted); white-space: nowrap; }
21
- & .crisp-taskstable-due[data-overdue] { color: var(--crisp-fg-danger); }
22
- & .crisp-taskstable-empty { padding: var(--crisp-space-8) var(--crisp-space-4); text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
23
- }
24
-
25
- @media (prefers-reduced-motion: reduce) {
26
- .crisp-taskstable, .crisp-taskstable * {
27
- animation-duration: 0.01ms !important;
28
- animation-iteration-count: 1 !important;
29
- transition-duration: 0.01ms !important;
30
- }
31
- }
32
- }
@@ -1 +0,0 @@
1
- export declare const companyLogos: Record<string, string>;
@@ -1,3 +0,0 @@
1
- export { type EditableOption, editableInputType, useEditableField, } from "./editable-field";
2
- export { type Column, type ColumnType, defaultColumns, defaultRows, type EntityValue, RecordsTable, type RecordsTableProps, type Row, tagTone, } from "./recordstable";
3
- export { recordstableRecipe } from "./recordstable.recipe";
@@ -1,289 +0,0 @@
1
- import * as React from "react";
2
- import { type EmptyStateLearnMore } from "../../components/emptystate";
3
- import type { EditableOption } from "./editable-field";
4
- type IconType = React.ComponentType<{
5
- size?: number;
6
- strokeWidth?: number;
7
- }>;
8
- export declare function isLabelTruncated(el: {
9
- scrollWidth: number;
10
- clientWidth: number;
11
- }): boolean;
12
- export type ColumnType = "entity" | "text" | "tags" | "number" | "url" | "date" | "currency" | "status" | "reference";
13
- export type StatusOption = {
14
- value: string;
15
- label: string;
16
- color: string;
17
- };
18
- export type ReferenceOption = {
19
- value: string;
20
- label: string;
21
- avatar?: string;
22
- };
23
- export type Column = {
24
- key: string;
25
- label: string;
26
- type?: ColumnType;
27
- /** Mark the column's values as AI-enriched — tints the cells the way
28
- * enriched values are washed (#9b69ff at 4%). */
29
- enriched?: boolean;
30
- icon?: IconType;
31
- /** This column's values reference another type of record (e.g. a
32
- * "Primary location" column of Country records). When the header
33
- * overflows, its `Tooltip` appends this type's icon and name after the
34
- * label and switches to Attio's second, reference-column tooltip chrome
35
- * (crisp#580) — distinct from `icon` above (the column's own field-type
36
- * glyph shown in the header row) and from `refOptions` (the picker's
37
- * selectable values). Omit for a plain column; the tooltip stays
38
- * label-only (#534). */
39
- referencedType?: {
40
- icon: IconType;
41
- label: string;
42
- };
43
- width?: number;
44
- /** Currency symbol for `currency` columns (default "$"). */
45
- symbol?: string;
46
- /** Choices for a `status` column (each a coloured dot + label). */
47
- options?: StatusOption[];
48
- /** Choices for a `reference` column (a searchable record picker). */
49
- refOptions?: ReferenceOption[];
50
- /** Adds a trailing "Create {createLabel}" row to the picker (Attio's own
51
- * always has one) — rendered only when this is provided. Picking it hands
52
- * back the picker's current search text, e.g. to open a create flow
53
- * pre-seeded with it. */
54
- onCreateReference?: (query: string) => void;
55
- /** The object noun in "Create {createLabel}" (Attio: "Create Person").
56
- * Only used with `onCreateReference`; defaults to "record". */
57
- createLabel?: string;
58
- /**
59
- * Full custom cell content — an escape hatch for a display crisp can't
60
- * express via `type` (sign-coloured currency, a maskable amount, a custom
61
- * badge, …). Precedence is **`render` > `format` > the built-in `type`
62
- * renderer**: when `render` is set it is the *only* thing drawn in the
63
- * cell, replacing whatever `type` would normally render.
64
- *
65
- * Interaction contract for a `render` column: it keeps every grid chrome —
66
- * row height, hairlines, padding, hover/selected tint, the cell focus ring,
67
- * shift-click range select, the right-click row menu — but is **never**
68
- * inline-editable and never shows a `type` picker (status/reference/date/
69
- * tags), because crisp has no way to write a value back into custom
70
- * content. Use `format` instead if the column should stay editable.
71
- *
72
- * ⌘C copies `format(value, row)` when the column also has one, otherwise
73
- * the raw value stringified the same way a plain column would — never a
74
- * serialization of the rendered node. Sort, filter, and the footer
75
- * aggregation all read `row[column.key]` directly and never call `render`.
76
- */
77
- render?: (value: unknown, row: Row) => React.ReactNode;
78
- /**
79
- * Display-only string transform. Unlike `render`, a `format` column keeps
80
- * the built-in `type` renderer's chrome (a `currency`/`number` column stays
81
- * right-aligned in its numeric span; `text`/`url`/`date`/the default case
82
- * keep their own wrapper) — `format`'s return value simply replaces the
83
- * text that renderer would otherwise show. `format` fully owns that text,
84
- * so a `currency` column's `format` should include its own symbol/masking
85
- * if it wants one (the built-in `$` prefix is skipped once `format` is
86
- * set). A `format` column stays editable/pickable exactly like a plain
87
- * `type` column — only `render` disables that.
88
- *
89
- * Sort, filter, and the footer aggregation all read the **raw**
90
- * `row[column.key]` value, never `format`'s output — so a grouped/prefixed
91
- * currency string still sums and sorts numerically.
92
- */
93
- format?: (value: unknown, row: Row) => string;
94
- /**
95
- * Opts this column into `RecordPanel`/`RecordPage`'s editable-field API
96
- * (#431) — click-to-edit there (not this table's own type-driven
97
- * mechanism, which those two components don't use). Ignored by
98
- * `RecordsTable` itself. A plain `editable` column, with no `editor`,
99
- * opens a text/number/date field (inferred from `type`, same inference
100
- * `DataTable`'s own `editable` columns use — #385); `editor` below picks
101
- * a `select`/`switch` picker instead. Both consumers require `onEdit` to
102
- * actually be given for a field to open at all.
103
- */
104
- editable?: boolean;
105
- /** `select`/`switch` render as the field's own live, always-interactive
106
- * content instead of opening on click — see `DataTableColumn.editor`
107
- * (#385) for the identical convention and reasoning. `select` reads
108
- * `editOptions`. */
109
- editor?: "select" | "switch";
110
- /** Options for `editor: "select"` — same shape `DataTable`'s own
111
- * `DataEditableOption` uses. Not `options` (`StatusOption` requires
112
- * `color`, for this type's own `status` picker) so this stays additive. */
113
- editOptions?: EditableOption[];
114
- };
115
- export type EntityValue = {
116
- name: string;
117
- avatar?: string;
118
- };
119
- export type Row = {
120
- id: string;
121
- } & Record<string, any>;
122
- export type RecordsTableProps = Omit<React.ComponentPropsWithoutRef<"div">, "children"> & {
123
- /** Partial override of the chrome strings; see `recordsTableLabels`. */
124
- labels?: RecordsTableLabelsInput;
125
- columns?: Column[];
126
- rows?: Row[];
127
- /** Controlled selection — omit for self-managed selection state. */
128
- selectedIds?: string[];
129
- onSelectionChange?: (ids: string[]) => void;
130
- /** Show the toolbar's "New" button (hide it when the page provides its own). */
131
- showNew?: boolean;
132
- /** Allow adding columns via the trailing "+" header button (default true). */
133
- allowAddColumn?: boolean;
134
- /** Called when a row's open (↗) button is pressed — opens a record panel. */
135
- onOpenRecord?: (row: Row) => void;
136
- /** Called when a row's entity name is clicked — navigate to its full record page. */
137
- onOpenRecordPage?: (row: Row) => void;
138
- /** Tone for `entity`/`reference` avatars: `"brand"` (uniform, today's
139
- * measured records-grid behaviour — the default) or `"hashed"` (a
140
- * stable colour per name, matching what `DataTable`'s `avatarTone` offers).
141
- * Entity avatars only — `TAG_TONES`/tag/status tone logic is untouched. */
142
- avatarTone?: "brand" | "hashed";
143
- /**
144
- * #603: Attio's zero-*records* state (nothing imported yet, no active
145
- * filter) adds an "Import CSV" / "Connect data source" pair and a
146
- * "Learn more" card next to the usual "New record" action -- measured
147
- * on Users/Workspaces, which show nothing else. Omit any of these and
148
- * that piece is left out; omit the whole prop and the empty state is
149
- * unchanged (just "New record"), since this hasn't been confirmed for
150
- * every object type.
151
- */
152
- emptyStateExtra?: {
153
- onImportCsv?: () => void;
154
- onConnectDataSource?: () => void;
155
- learnMore?: EmptyStateLearnMore;
156
- };
157
- className?: string;
158
- };
159
- export declare function tagTone(t: string): string;
160
- type Agg = "none" | "count" | "filled" | "sum" | "avg" | "min" | "max";
161
- /**
162
- * Every chrome string RecordsTable renders. Supplied as a PARTIAL map, so
163
- * translating one menu item does not mean restating the other fifty; anything
164
- * omitted keeps its English default from `recordsTableLabels`.
165
- *
166
- * The interpolated ones are formatters rather than templates because word
167
- * order does not survive translation — "Sorted by Name +2" has no stable
168
- * arrangement of its parts across languages (the same reason TableToolbar's
169
- * triggers take formatters, #77).
170
- */
171
- export type RecordsTableLabels = {
172
- selectAll: string;
173
- sort: string;
174
- sortField: string;
175
- sortDirection: string;
176
- removeSort: string;
177
- reorderSort: string;
178
- addSort: string;
179
- ascending: string;
180
- descending: string;
181
- noSorts: string;
182
- learnSorting: string;
183
- /**
184
- * Where the sort popover's "Learn about sorting" row links to. Unset by default:
185
- * the row renders as plain text, since a component library has no help site of its
186
- * own to point at. Pass your own docs URL to make it a link.
187
- */
188
- learnSortingHref?: string;
189
- filter: string;
190
- addFilter: string;
191
- filterByField: string;
192
- filterByPlaceholder: string;
193
- filterValuePlaceholder: string;
194
- selectCondition: string;
195
- clearFilter: string;
196
- addColumn: string;
197
- columnLabel: string;
198
- editColumnLabel: string;
199
- hideFromView: string;
200
- moveLeft: string;
201
- moveRight: string;
202
- sortAscending: string;
203
- sortDescending: string;
204
- newRecord: string;
205
- createRecord: string;
206
- createMore: string;
207
- newButton: string;
208
- more: string;
209
- moreActions: string;
210
- recordActions: string;
211
- copy: string;
212
- duplicate: string;
213
- addToList: string;
214
- inAList: string;
215
- /** Placeholder in the record search field. */
216
- searchRecordsPlaceholder: string;
217
- /** Placeholder in a select cell's option search. */
218
- searchOptionsPlaceholder: string;
219
- /** Prefix on the bulk bar's selected count. */
220
- selectedCount: string;
221
- /**
222
- * Announced when the selection changes. A formatter, not a string: the
223
- * count moves within the sentence between languages. Return "" to say
224
- * nothing. The bulk bar cannot carry this itself — the consumer mounts it
225
- * when the first row is picked, so its live region would be created by the
226
- * very change it should announce (#234).
227
- */
228
- selectionAnnouncement: (count: number) => string;
229
- delete: string;
230
- sendEmail: string;
231
- runWorkflow: string;
232
- calculate: string;
233
- emptyPlaceholder: string;
234
- setRecordPlaceholder: string;
235
- noRecords: string;
236
- untitled: string;
237
- /** #603: the zero-records state's optional extra CTAs. */
238
- importCsv: string;
239
- connectDataSource: string;
240
- /** Aggregation names, keyed by the aggregation. */
241
- agg: Record<Agg, string>;
242
- /** Filter operator names, keyed by the operator. */
243
- op: Record<FilterOp, string>;
244
- /** Title of the create dialog, given the entity column's label. */
245
- newRecordTitle: (entity: string) => string;
246
- /** Placeholder for a create-dialog field, given the field's label. */
247
- setFieldPlaceholder: (field: string) => string;
248
- /** Label for a newly added column, given how many columns exist. */
249
- newColumnLabel: (index: number) => string;
250
- /** The sort trigger, given the first sorted column and how many more. */
251
- sortedBy: (first: string, extra: number) => string;
252
- /** The filter trigger, given how many filters are active. */
253
- filterCount: (active: number) => string;
254
- /** The row count under the table. */
255
- recordCount: (n: number) => string;
256
- /** Heading above a filter chip's condition editor. */
257
- conditionTitle: (field: string) => string;
258
- };
259
- export declare const recordsTableLabels: RecordsTableLabels;
260
- /** Partial override of any chrome string; the two keyed maps nest. */
261
- export type RecordsTableLabelsInput = Partial<Omit<RecordsTableLabels, "agg" | "op">> & {
262
- agg?: Partial<Record<Agg, string>>;
263
- op?: Partial<Record<FilterOp, string>>;
264
- };
265
- type FilterOp = "contains" | "doesnotcontain" | "startswith" | "endswith" | "is" | "isnot" | "empty" | "notempty";
266
- /**
267
- * A date-only cell is a CALENDAR DATE, not an instant. ECMA-262 parses the
268
- * date-only ISO forms — `1998`, `1998-09`, `1998-09-04` — as UTC midnight, and
269
- * the field then renders that instant in the reader's own timezone. A
270
- * `founded: "1998"` field therefore read **Dec 31, 1997** for every reader west
271
- * of UTC, at every hour of every day, and a date picked east of UTC
272
- * round-tripped back a day short through `toISOString()` (#220).
273
- *
274
- * A string that carries a time is left alone: that one really is an instant,
275
- * and shifting it would move the moment it names.
276
- */
277
- import { formatCellDate, parseCellDate } from "../../core";
278
- export { formatCellDate, parseCellDate };
279
- /**
280
- * A measured-style record grid — fully featured. Toolbar (search / sort status /
281
- * count / New), sticky checkbox + entity columns, 36px rows, typed cells,
282
- * click-to-sort headers, column resize by dragging a header edge, inline cell
283
- * editing (double-click a text/number/url/date cell), add-row and add-column,
284
- * and a floating bulk-action bar (delete / deselect) when rows are selected.
285
- * Selection can be controlled via `selectedIds`/`onSelectionChange`.
286
- */
287
- export declare function RecordsTable({ labels, columns: columnsProp, rows: rowsProp, selectedIds, onSelectionChange, showNew, allowAddColumn, onOpenRecord, onOpenRecordPage, avatarTone, emptyStateExtra, className, ...rest }: RecordsTableProps): React.JSX.Element;
288
- export declare const defaultColumns: Column[];
289
- export declare const defaultRows: Row[];
@@ -1,5 +0,0 @@
1
- declare const config: import("../..").RecipeConfig<{}>;
2
- export declare const recordstableRecipe: import("../..").RecipeFn<{}> & {
3
- config: import("../..").RecipeConfig<{}>;
4
- };
5
- export default config;