@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,172 +0,0 @@
1
- import { toolbarChromeBody, toolbarChromeHead, } from "../../components/tabletoolbar/tabletoolbar.chrome.js";
2
- import { defineRecipe, resolveRecipe, rtlFlipCss } from "../../core/index.js";
3
- import { contract as c } from "../../tokens/index.js";
4
- import { recordTagCss } from "./recordstable.tag.js";
5
- // A record grid, measured: 36px rows, 1px borderSubtle (#eeeff1) grid
6
- // lines on BOTH axes, sticky checkbox + entity (first) columns, entity = 16px
7
- // avatar + 14px/500 name, 28px toolbar controls, a floating bulk-action bar.
8
- //
9
- // The toolbar's measured chrome now lives in the TableToolbar primitive
10
- // (components/tabletoolbar/tabletoolbar.chrome.ts) and is emitted below under
11
- // this surface's own class prefix, so RecordsTable's markup is unchanged. The
12
- // three rules between the two halves are RecordsTable's own and must stay
13
- // mid-block: they depend on following the .menu rule in source order.
14
- const config = defineRecipe({
15
- name: "recordstable",
16
- base: `
17
- box-sizing: border-box; position: relative; display: flex; flex-direction: column; min-height: 0; height: 100%; width: 100%; font-family: ${c.font.sans}; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
18
- & * { box-sizing: border-box; }
19
-
20
- /* toolbar */
21
- ${toolbarChromeHead("recordstable")}
22
- & .crisp-recordstable-ctx { z-index: 60; }
23
- & .crisp-recordstable-th[data-drop] { box-shadow: inset 2px 0 0 ${c.bgBrandSolid}; }
24
- & .crisp-recordstable-th-inner[draggable="true"]:active { cursor: grabbing; }
25
- ${toolbarChromeBody("recordstable")}
26
-
27
- /* scroll frame + table */
28
- & .crisp-recordstable-scroll { flex: 1; min-height: 0; overflow: auto; background: ${c.bgSurface}; border-top: 1px solid ${c.borderSubtle}; scrollbar-width: thin; scrollbar-color: color-mix(in srgb, ${c.fgPrimary} 22%, transparent) transparent; }
29
- & .crisp-recordstable-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
30
- & .crisp-recordstable-scroll::-webkit-scrollbar-thumb { background: color-mix(in srgb, ${c.fgPrimary} 20%, transparent); border-radius: ${c.radius.full}; border: 2px solid ${c.bgSurface}; background-clip: padding-box; }
31
- & .crisp-recordstable-scroll::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, ${c.fgPrimary} 34%, transparent); background-clip: padding-box; }
32
- & .crisp-recordstable-scroll::-webkit-scrollbar-corner { background: transparent; }
33
- & .crisp-recordstable-table { table-layout: fixed; border-collapse: separate; border-spacing: 0; }
34
- & .crisp-recordstable-th, & .crisp-recordstable-td { position: relative; height: ${c.size.tableRow}; padding: 0 ${c.space["2"]}; border-bottom: 1px solid ${c.borderSubtle}; border-inline-end: 1px solid ${c.borderSubtle}; text-align: start; white-space: nowrap; overflow: hidden; }
35
- & .crisp-recordstable-th { position: sticky; top: 0; z-index: 2; padding: 0; height: ${c.size.tableHeader}; background: ${c.bgSurface}; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; letter-spacing: -0.14px; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); user-select: none; }
36
- & .crisp-recordstable-th-inner { display: flex; align-items: center; gap: ${c.space["1.5"]}; width: 100%; height: 100%; margin: 0; padding: 0 ${c.space["2"]}; border: none; background: none; color: inherit; font: inherit; letter-spacing: inherit; text-align: start; cursor: pointer; }
37
- & .crisp-recordstable-th-inner svg { flex-shrink: 0; color: color-mix(in srgb, ${c.fgPrimary} 45%, transparent); }
38
- & .crisp-recordstable-th-label { flex: 1; overflow: hidden; text-overflow: ellipsis; }
39
- & .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; }
40
- & .crisp-recordstable-th-rename:focus { box-shadow: inset 0 0 0 1px ${c.bgBrandSolid}; }
41
- & .crisp-recordstable-sort { flex-shrink: 0; opacity: 0.4; transition: opacity .12s; color: color-mix(in srgb, ${c.fgPrimary} 45%, transparent); }
42
- & .crisp-recordstable-th:hover .crisp-recordstable-sort, & .crisp-recordstable-th[data-sorted] .crisp-recordstable-sort { opacity: 1; }
43
- & .crisp-recordstable-th[data-sorted="desc"] .crisp-recordstable-sort svg { transform: rotate(180deg); }
44
-
45
- /* column resize handle */
46
- & .crisp-recordstable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
47
- & .crisp-recordstable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: ${c.bgBrandSolid}; opacity: 0; transition: opacity .12s; }
48
- & .crisp-recordstable-resize:hover::after, & .crisp-recordstable-resize[data-resizing]::after { opacity: 1; }
49
-
50
- /* add-column header */
51
- & .crisp-recordstable-addcol { min-width: 44px; }
52
- & .crisp-recordstable-addcol button { display: flex; align-items: center; justify-content: center; width: 44px; height: 100%; border: none; background: none; color: ${c.fgMuted}; cursor: pointer; }
53
- & .crisp-recordstable-addcol button:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
54
-
55
- /* cells */
56
- /* font-weight is explicit because the first cell of each row is a
57
- th[scope=row] — the record's name is what the row IS — and a th
58
- defaults to bold. Measured before the change and unchanged after: 400. */
59
- & .crisp-recordstable-td { font-size: ${c.text.lg}; font-weight: ${c.weight.regular}; color: ${c.fgPrimary}; background: ${c.bgSurface}; cursor: default; }
60
- & .crisp-recordstable-td--editable { cursor: text; }
61
- /* keyboard/click cell selection (spreadsheet-style) */
62
- & .crisp-recordstable-td[data-selected] { outline: 2px solid ${c.bgBrandSolid}; outline-offset: -2px; border-radius: ${c.radius.sm}; z-index: 3; }
63
- & .crisp-recordstable-scroll:focus { outline: none; }
64
- /* picker cells (status / date / reference) host a ghost control edge-to-edge */
65
- & .crisp-recordstable-td--picker { padding: 0; }
66
- & .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 ${c.space["2"]}; }
67
- & .crisp-recordstable-td--picker .crisp-multiselect-trigger:hover { background: none; }
68
- & .crisp-recordstable-td--picker .crisp-multiselect-trigger[data-open] { box-shadow: inset 0 0 0 1.5px ${c.bgBrandSolid}; }
69
- /* tag cell: one compact clipped line of full chips, no chevron (opens the picker on click) */
70
- & .crisp-recordstable-td--picker .crisp-multiselect-chips { flex-wrap: nowrap; overflow: hidden; }
71
- & .crisp-recordstable-td--picker .crisp-multiselect-chips .crisp-recordstable-tag { flex-shrink: 0; max-width: none; }
72
- & .crisp-recordstable-td--picker .crisp-multiselect-chevron { display: none; }
73
- & .crisp-recordstable-td--picker .crisp-multiselect-placeholder { color: ${c.fgMuted}; }
74
- /* reference picker: a leading 16px avatar per option, plus a trailing
75
- "Create X" row when the consumer supplies one (#449) -- measured live
76
- against Attio's own picker (16px circular avatar, 16px plain icon for
77
- the create row, both sitting in the option's existing 8px gap, no new
78
- gap value invented). */
79
- & .crisp-recordstable-refoption { display: flex; align-items: center; gap: ${c.space["2"]}; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
80
- & .crisp-recordstable-refoption-avatar { flex-shrink: 0; }
81
- & .crisp-recordstable-refoption--create { color: ${c.fgMuted}; }
82
- & .crisp-recordstable-status { display: inline-flex; align-items: center; gap: ${c.space["1.5"]}; min-width: 0; }
83
- & .crisp-recordstable-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: ${c.radius.full}; }
84
- & .crisp-recordstable-cell { display: flex; align-items: center; gap: ${c.space["2"]}; height: 100%; min-width: 0; }
85
- & .crisp-recordstable-cell--num { justify-content: flex-end; font-variant-numeric: tabular-nums; }
86
- & .crisp-recordstable-cell--tags { gap: ${c.space["1"]}; }
87
- ${recordTagCss}
88
- & .crisp-recordstable-muted { color: ${c.fgMuted}; }
89
- & .crisp-recordstable-entity { display: flex; align-items: center; gap: ${c.space["1.5"]}; min-width: 0; color: ${c.fgPrimary}; font-weight: ${c.weight.medium}; text-decoration: none; }
90
- & .crisp-recordstable-entity:hover .crisp-recordstable-entity-name { text-decoration: underline; }
91
- & .crisp-recordstable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
92
- & 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%; }
93
- & .crisp-recordstable-fav { width: ${c.size.icon}; height: ${c.size.icon}; border-radius: ${c.radius.sm}; flex-shrink: 0; object-fit: cover; background: ${c.bgRaised}; }
94
- & .crisp-recordstable-open { position: absolute; inset-inline-end: ${c.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: ${c.radius.sm}; background: ${c.bgSurface}; box-shadow: inset 0 0 0 1px ${c.borderDefault}; color: ${c.fgMuted}; font-family: inherit; cursor: pointer; opacity: 0; transition: opacity .12s, background-color .12s; z-index: 3; }
95
- & .crisp-recordstable-open:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
96
- & tbody tr:hover .crisp-recordstable-open { opacity: 1; }
97
- & .crisp-recordstable-link { color: ${c.fgPrimary}; text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
98
- & .crisp-recordstable-link:hover { text-decoration: underline; }
99
-
100
- /* inline edit input */
101
- & .crisp-recordstable-edit { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0 ${c.space["2"]}; border: none; outline: 2px solid ${c.bgBrandSolid}; outline-offset: -2px; border-radius: ${c.radius.sm}; background: ${c.bgSurface}; font-family: inherit; font-size: ${c.text.lg}; color: ${c.fgPrimary}; z-index: 5; }
102
- & .crisp-recordstable-edit--num { text-align: end; font-variant-numeric: tabular-nums; }
103
-
104
- /* selection / hover / sticky */
105
- & .crisp-recordstable-col-check { width: 40px; min-width: 40px; padding: 0; }
106
- & .crisp-recordstable-check-inner { display: flex; align-items: center; justify-content: center; height: 100%; }
107
- & .crisp-recordstable-col-check, & .crisp-recordstable-col-entity { position: sticky; z-index: 3; background: ${c.bgSurface}; }
108
- /* The sticky pin is a LOGICAL offset: with physical \`left\` these columns
109
- were not pinned at all under RTL -- scrolled, they left the viewport
110
- entirely (measured: viewport 13..773, check column at x=1333). Nothing in
111
- JS measures these, so converting them is safe; the two properties below
112
- that have no logical form get an explicit RTL rule rather than being
113
- left to point the wrong way. */
114
- & .crisp-recordstable-col-check { inset-inline-start: 0; }
115
- & .crisp-recordstable-col-entity { inset-inline-start: 40px; clip-path: inset(0 -38px 0 0); transition: box-shadow .2s ease; }
116
- [dir="rtl"] & .crisp-recordstable-col-entity { clip-path: inset(0 0 0 -38px); }
117
- & thead .crisp-recordstable-col-check, & thead .crisp-recordstable-col-entity { z-index: 4; }
118
- /* soft shadow on the sticky column edge once the grid is scrolled horizontally */
119
- & .crisp-recordstable-scroll[data-scrolled] .crisp-recordstable-col-entity { box-shadow: 6px 0 16px 4px rgba(0,0,0,0.12); }
120
- /* box-shadow has no logical form either: the bleed follows the scrolled
121
- content, which is the opposite side under RTL. */
122
- [dir="rtl"] & .crisp-recordstable-scroll[data-scrolled] .crisp-recordstable-col-entity { box-shadow: -6px 0 16px 4px rgba(0,0,0,0.12); }
123
- /* AI-enriched values carry a purple wash. Measured live:
124
- a full-bleed overlay at #9b69ff 4% (== bgPurple), composited OVER
125
- whatever row-state background the cell already has, which is why it
126
- is an ::after and not a background. */
127
- & .crisp-recordstable-td[data-enriched]::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: color-mix(in srgb, ${c.bgPurple} 4%, transparent); }
128
- & tbody tr:hover .crisp-recordstable-td { background: color-mix(in srgb, ${c.fgPrimary} 4%, ${c.bgSurface}); }
129
- & tbody tr[data-selected] .crisp-recordstable-td { background: color-mix(in srgb, ${c.bgBrandSolid} 10%, ${c.bgSurface}); }
130
- & tbody tr[data-selected]:hover .crisp-recordstable-td { background: color-mix(in srgb, ${c.fgPrimary} 4%, color-mix(in srgb, ${c.bgBrandSolid} 10%, ${c.bgSurface})); }
131
- & .crisp-recordstable-th:last-child, & .crisp-recordstable-td:last-child { border-inline-end: none; }
132
-
133
- /* add-row footer */
134
- & .crisp-recordstable-addrow { display: flex; align-items: center; gap: ${c.space["2"]}; width: 100%; height: ${c.size.tableRow}; padding: 0 ${c.space["2"]}; border: none; border-top: 1px solid ${c.borderSubtle}; background: ${c.bgSurface}; color: ${c.fgMuted}; font-family: inherit; font-size: ${c.text.lg}; cursor: pointer; }
135
- & .crisp-recordstable-addrow:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
136
- /* zero-row empty state — centred in the body region (headers + summary stay) */
137
- & .crisp-recordstable-empty { display: flex; align-items: center; justify-content: center; min-height: 320px; padding: ${c.space["8"]} ${c.space["4"]}; }
138
-
139
- /* summary footer — per-column aggregation, sticky to the bottom */
140
- & .crisp-recordstable-sum { position: sticky; bottom: 0; z-index: 2; height: ${c.size.tableRow}; padding: 0; border-top: 1px solid ${c.borderSubtle}; border-bottom: 1px solid ${c.borderSubtle}; border-inline-end: 1px solid ${c.borderSubtle}; background: ${c.bgSurface}; }
141
- & tfoot .crisp-recordstable-col-check, & tfoot .crisp-recordstable-col-entity { position: sticky; bottom: 0; z-index: 3; }
142
- & .crisp-recordstable-sum:last-child { border-inline-end: none; }
143
- & .crisp-recordstable-sum-btn { display: flex; align-items: center; justify-content: flex-end; gap: ${c.space["1.5"]}; width: 100%; height: 100%; margin: 0; padding: 0 ${c.space["3"]}; border: none; background: none; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; cursor: pointer; white-space: nowrap; overflow: hidden; }
144
- & .crisp-recordstable-sum-btn:hover { background: ${c.bgRaised}; }
145
- & .crisp-recordstable-sum-label { color: color-mix(in srgb, ${c.fgPrimary} 45%, transparent); text-transform: capitalize; }
146
- & .crisp-recordstable-sum-value { color: ${c.fgPrimary}; font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; }
147
- & .crisp-recordstable-sumgroup { width: 100%; height: 100%; }
148
- & .crisp-recordstable-pin { flex-shrink: 0; margin-inline-start: ${c.space["1"]}; color: ${c.bgWarning}; fill: color-mix(in srgb, ${c.bgWarning} 35%, transparent); }
149
- & .crisp-recordstable-sum-btn[data-empty] { opacity: 0; justify-content: flex-start; }
150
- & .crisp-recordstable-sum-btn[data-empty]:hover { opacity: 1; }
151
-
152
- /* bulk action bar lives in its own component now — see components/bulkbar */
153
- & .crisp-recordstable-bulk-sep { width: 1px; height: 20px; margin: 0 ${c.space["0.5"]}; background: ${c.borderSubtle}; }
154
- & .crisp-recordstable-bulk-close { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; border: none; border-radius: ${c.radius.md}; background: transparent; color: ${c.fgMuted}; cursor: pointer; }
155
- & .crisp-recordstable-bulk-close:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
156
-
157
- /* announced, never drawn */
158
- & .crisp-recordstable-live {
159
- position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
160
- overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
161
- }
162
-
163
- ${rtlFlipCss}
164
- `,
165
- variants: {},
166
- defaultVariants: {},
167
- compoundVariants: [],
168
- });
169
- export const recordstableRecipe = Object.assign(resolveRecipe(config), {
170
- config,
171
- });
172
- export default config;
@@ -1,23 +0,0 @@
1
- import { contract as c } from "../../tokens/index.js";
2
- /**
3
- * The record tag/chip, measured live: 22px tall, `padding: 0 4px`,
4
- * `border-radius: ${c.radius.chip}`, 14px/20px at weight 500, and a 1px inset ring in the
5
- * tone's colour.
6
- *
7
- * Its own module (not pasted into `recordstable.recipe.ts`) because it used
8
- * to be shared with `RecordPanel`'s own Details field list too, and the two
9
- * copies had already started to drift before that sharing existed — adding
10
- * the measured inset ring meant patching the same rules twice. `RecordPanel`
11
- * no longer renders a Details list (#448, Attio's own peek doesn't have
12
- * one), so `RecordsTable` is this module's only consumer now; kept as its
13
- * own file rather than folded back in, since a second consumer showing up
14
- * again is exactly how the drift happened the first time.
15
- */
16
- export const recordTagCss = `& .crisp-recordstable-tag { display: inline-flex; align-items: center; height: ${c.size.chip}; padding: 0 ${c.space["1"]}; border-radius: ${c.radius.chip}; font-size: ${c.text.lg}; line-height: 20px; font-weight: ${c.weight.medium}; white-space: nowrap; background: color-mix(in srgb, ${c.fgPrimary} 6%, transparent); color: color-mix(in srgb, ${c.fgPrimary} 82%, transparent); box-shadow: inset 0 0 0 1px ${c.borderSubtle}; }
17
- & .crisp-recordstable-tag[data-tone="brand"] { background: color-mix(in srgb, ${c.bgBrandSolid} 14%, transparent); color: color-mix(in srgb, ${c.bgBrandSolid} 82%, ${c.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.bgBrandSolid} 30%, transparent); }
18
- & .crisp-recordstable-tag[data-tone="success"] { background: color-mix(in srgb, ${c.bgSuccess} 16%, transparent); color: color-mix(in srgb, ${c.bgSuccess} 78%, ${c.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.bgSuccess} 30%, transparent); }
19
- & .crisp-recordstable-tag[data-tone="warning"] { background: color-mix(in srgb, ${c.bgWarning} 18%, transparent); color: color-mix(in srgb, ${c.bgWarning} 82%, ${c.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.bgWarning} 30%, transparent); }
20
- & .crisp-recordstable-tag[data-tone="danger"] { background: color-mix(in srgb, ${c.bgDanger} 15%, transparent); color: color-mix(in srgb, ${c.bgDanger} 80%, ${c.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.bgDanger} 30%, transparent); }
21
- & .crisp-recordstable-tag[data-tone="purple"] { background: color-mix(in srgb, ${c.bgPurple} 16%, transparent); color: color-mix(in srgb, ${c.bgPurple} 80%, ${c.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.bgPurple} 30%, transparent); }
22
- & .crisp-recordstable-tag[data-tone="cyan"] { background: color-mix(in srgb, ${c.bgCyan} 18%, transparent); color: color-mix(in srgb, ${c.bgCyan} 82%, ${c.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.bgCyan} 30%, transparent); }
23
- & .crisp-recordstable-tag[data-tone="orange"] { background: color-mix(in srgb, ${c.bgOrange} 16%, transparent); color: color-mix(in srgb, ${c.bgOrange} 82%, ${c.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.bgOrange} 30%, transparent); }`;
@@ -1,2 +0,0 @@
1
- export { TasksTable, } from "./taskstable.js";
2
- export { taskstableRecipe } from "./taskstable.recipe.js";
@@ -1,24 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cx, useCrispMessage, useCrispMessages } from "../../core/index.js";
3
- import { ListRow, listpageRecipe } from "../listpage/index.js";
4
- import { taskstableRecipe } from "./taskstable.recipe.js";
5
- function Cell({ flex, className, children, }) {
6
- return (_jsx("span", { className: cx("crisp-taskstable-cell", className), style: { flex }, children: children }));
7
- }
8
- /**
9
- * A grouped due-date table pattern — Attio's Tasks page. Composes
10
- * `ListPage`'s toolbar and group-row chrome (both class-compatible; see
11
- * taskstable.recipe.ts) with a typed column/cell layer: a title+checkbox
12
- * row grouped into due-date sections, a due-date cell that goes red past
13
- * due, and consumer-supplied record/assignee chip slots.
14
- */
15
- export function TasksTable({ columns, groups, leading, trailing, emptyLabel: emptyLabelProp, completeLabel: completeLabelProp, incompleteLabel: incompleteLabelProp, className, ...rest }) {
16
- const crispMessages = useCrispMessages();
17
- const emptyLabel = emptyLabelProp ?? crispMessages["No tasks"] ?? "No tasks";
18
- const completeLabel = useCrispMessage(completeLabelProp, "Mark complete");
19
- const incompleteLabel = useCrispMessage(incompleteLabelProp, "Mark incomplete");
20
- const flexOf = (key) => columns.find((col) => col.key === key)?.flex ?? 1;
21
- const total = groups.reduce((s, g) => s + g.rows.length, 0);
22
- return (_jsxs("div", { className: cx(taskstableRecipe({ className }), listpageRecipe()), ...rest, children: [_jsxs("div", { className: "crisp-listpage-toolbar", children: [_jsx("div", { className: "crisp-listpage-toolbar-lead", children: leading }), trailing != null && (_jsx("div", { className: "crisp-listpage-toolbar-trail", children: trailing }))] }), _jsxs("div", { className: "crisp-listpage-body", children: [_jsx("div", { className: "crisp-taskstable-colhead", children: columns.map((col) => (_jsxs("span", { className: "crisp-taskstable-colhead-cell", style: { flex: col.flex ?? 1 }, children: [col.icon, col.label] }, col.key))) }), total === 0 ? (_jsx("div", { className: "crisp-taskstable-empty", children: emptyLabel })) : (groups.map((g) => (_jsxs("div", { children: [_jsxs("div", { className: "crisp-listpage-group", children: [g.label, _jsx("span", { className: "crisp-listpage-group-count", children: g.count })] }), g.rows.map((row) => (_jsx(ListRow, { children: _jsxs("div", { className: "crisp-taskstable-row", style: { flex: 1 }, children: [_jsxs(Cell, { flex: flexOf("title"), children: [_jsx("input", { type: "checkbox", className: "crisp-taskstable-check", checked: !!row.done, "aria-label": row.done ? incompleteLabel : completeLabel, onChange: () => row.onToggle?.(!row.done) }), _jsx("span", { className: "crisp-taskstable-title", children: row.title })] }), _jsx(Cell, { flex: flexOf("due"), children: row.due != null && (_jsx("span", { className: "crisp-taskstable-due", "data-overdue": row.due.overdue ? "" : undefined, children: row.due.label })) }), _jsx(Cell, { flex: flexOf("record"), children: row.record }), _jsx(Cell, { flex: flexOf("assignee"), children: row.assignee })] }) }, row.id)))] }, g.id))))] })] }));
23
- }
24
- TasksTable.displayName = "TasksTable";
@@ -1,53 +0,0 @@
1
- import { defineRecipe, resolveRecipe } from "../../core/index.js";
2
- import { contract as c } from "../../tokens/index.js";
3
- // A grouped due-date table — Attio's Tasks page (app.attio.com/<ws>/tasks),
4
- // measured live and recorded on crisp#462. Composes ListPage's own toolbar
5
- // (`.crisp-listpage-toolbar`) and group-row (`.crisp-listpage-group`) chrome
6
- // by carrying BOTH this recipe's root class and `listpageRecipe`'s root class
7
- // on the same element (see taskstable.tsx) -- both recipes' descendant rules
8
- // nest under their own root class, so this is a safe, ordinary composition,
9
- // the same way `<ListRow>` already lets a caller compose into `ListPage`'s
10
- // group bodies. Real deviation: ListPage's own `columns` colhead slot is
11
- // pinned to `c.size.menuRow` (32px, correct for its Reports/Sequences
12
- // reference surfaces per #479) but Tasks/Notes independently measure a 49px
13
- // column-header row (#462) -- ListPage's colhead can't be asked to render at
14
- // a different height per consumer today, so this component renders its own
15
- // colhead instead of using that slot. Flagged in the PR, not fixed here (out
16
- // of scope: don't change ListPage for this component).
17
- const config = defineRecipe({
18
- name: "taskstable",
19
- base: `display: flex; flex-direction: column; min-height: 0; box-sizing: border-box; font-family: ${c.font.sans}; color: ${c.fgPrimary};
20
- -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
21
-
22
- & .crisp-taskstable-colhead { display: flex; align-items: center; gap: ${c.space["1.5"]}; min-height: 49px; max-height: 49px; padding: ${c.space["2.5"]} ${c.space["3"]}; box-shadow: inset 0 -1px 0 ${c.borderSubtle}; font-size: ${c.text.sm}; color: ${c.fgMuted}; }
23
- & .crisp-taskstable-colhead-cell { display: inline-flex; align-items: center; gap: ${c.space["1.5"]}; min-width: 0; }
24
-
25
- & .crisp-taskstable-row { display: flex; align-items: center; gap: ${c.space["1.5"]}; }
26
- /* #642: measured live -- title, due date and assignee name are ALL
27
- 14px/500 on Attio's own Tasks page. Only -title/-due set their own
28
- font-size (and only -title its own weight), so an unstyled consumer
29
- slot (a plain PersonChip/RecordChip name span with no font-size of
30
- its own) fell through to the browser/body default, 16px/400. Set
31
- the ambient font here so it cascades to any slot content that
32
- doesn't override it -- -title/-due's own declarations still apply,
33
- unchanged, they're just now redundant with this baseline rather
34
- than the only source of it. */
35
- & .crisp-taskstable-cell { display: flex; align-items: center; min-width: 0; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; }
36
-
37
- & .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: ${c.radius.full}; background: ${c.bgSurface}; box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 9%, transparent); cursor: pointer; transition: background-color .12s, box-shadow .12s; }
38
- & .crisp-taskstable-check:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 30%, transparent); }
39
- & .crisp-taskstable-check:checked { background: ${c.bgBrandSolid} 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"); }
40
- & .crisp-taskstable-check:focus-visible { outline: 2px solid ${c.bgBrandSolid}; outline-offset: 1px; }
41
-
42
- & .crisp-taskstable-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: ${c.fgPrimary}; }
43
-
44
- & .crisp-taskstable-due { font-size: ${c.text.lg}; color: ${c.fgMuted}; white-space: nowrap; }
45
- & .crisp-taskstable-due[data-overdue] { color: ${c.fgDanger}; }
46
-
47
- & .crisp-taskstable-empty { padding: ${c.space["8"]} ${c.space["4"]}; text-align: center; font-size: ${c.text.md}; color: ${c.fgMuted}; }`,
48
- variants: {},
49
- defaultVariants: {},
50
- compoundVariants: [],
51
- });
52
- export const taskstableRecipe = resolveRecipe(config);
53
- export default config;
@@ -1,223 +0,0 @@
1
- .crisp-recordstable {
2
- 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;
3
- & * { box-sizing: border-box; }
4
- /* toolbar */
5
- & .crisp-recordstable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); padding: 0 var(--crisp-space-3) var(--crisp-space-2); }
6
- & .crisp-recordstable-toolbar[data-inline] { padding-bottom: 0; }
7
- & .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); }
8
- & .crisp-recordstable-search svg { flex-shrink: 0; }
9
- & .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; }
10
- & .crisp-recordstable-search input::placeholder { color: var(--crisp-fg-muted); }
11
- & .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; }
12
- & .crisp-recordstable-tool:hover { background: var(--crisp-bg-raised); }
13
- & .crisp-recordstable-tool svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
14
- & .crisp-recordstable-tool--active { background: var(--crisp-bg-raised); }
15
- & .crisp-recordstable-tool--ghost { box-shadow: none; background: color-mix(in srgb, var(--crisp-fg-primary) 3%, transparent); color: var(--crisp-fg-muted); }
16
- & .crisp-recordstable-tool--ghost:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); }
17
- & .crisp-recordstable-toolgroup { position: relative; display: inline-flex; }
18
- & .crisp-recordstable-divider { flex-shrink: 0; width: 1px; height: 20px; background: var(--crisp-border-subtle); }
19
- & .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; }
20
- & .crisp-recordstable-menu { @starting-style { opacity: 0; transform: scale(0.96); } }
21
- & .crisp-recordstable-ctx { z-index: 60; }
22
- & .crisp-recordstable-th[data-drop] { box-shadow: inset 2px 0 0 var(--crisp-bg-brand-solid); }
23
- & .crisp-recordstable-th-inner[draggable="true"]:active { cursor: grabbing; }
24
- & .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); }
25
- & .crisp-recordstable-sortpop { min-width: 480px; overflow: visible; padding: var(--crisp-space-3) var(--crisp-space-1-5); gap: var(--crisp-space-2); }
26
- & .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); }
27
- & .crisp-recordstable-sort-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); }
28
- & .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; }
29
- & .crisp-recordstable-sort-grip:active { cursor: grabbing; }
30
- & .crisp-recordstable-sort-grip:hover { color: var(--crisp-fg-primary); }
31
- & .crisp-recordstable-sort-optlabel { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
32
- & .crisp-recordstable-sort-optlabel svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
33
- & .crisp-recordstable-sort-field { flex: 1; min-width: 0; }
34
- & .crisp-recordstable-sort-dir { width: 136px; flex-shrink: 0; }
35
- & .crisp-recordstable-sort-field .crisp-select-trigger, & .crisp-recordstable-sort-dir .crisp-select-trigger { height: 28px; padding-right: var(--crisp-space-1); }
36
- & .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; }
37
- & .crisp-recordstable-sort-remove:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
38
- & .crisp-recordstable-sort-addrow { display: flex; padding: var(--crisp-space-0-5) 0; }
39
- & .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; }
40
- & .crisp-recordstable-sort-add:hover { background: var(--crisp-bg-raised); }
41
- & .crisp-recordstable-sort-add svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
42
- & .crisp-recordstable-sort-foot { display: flex; padding: var(--crisp-space-1-5) var(--crisp-space-1) var(--crisp-space-0-5); }
43
- & .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; }
44
- & .crisp-recordstable-sort-learn svg { flex-shrink: 0; }
45
- & .crisp-recordstable-sort-learn:hover { color: var(--crisp-fg-primary); }
46
- & .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; }
47
- & .crisp-recordstable-menu-item:hover { background: var(--crisp-bg-raised); }
48
- & .crisp-recordstable-menu-item:disabled { opacity: 0.4; cursor: default; }
49
- & .crisp-recordstable-menu-item:disabled:hover { background: none; }
50
- & .crisp-recordstable-menu-item svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
51
- & .crisp-recordstable-menu-item[data-on] svg { color: var(--crisp-bg-brand-solid); }
52
- & .crisp-recordstable-menu-item .crisp-recordstable-menu-check { margin-left: auto; }
53
- & .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; }
54
- & .crisp-recordstable-menu-sep { height: 1px; border: none; background: var(--crisp-border-subtle); margin: var(--crisp-space-0-5) 0; }
55
- /* #592: the Filter field picker's nested-scope header (back control +
56
- search, once drilled into a nestedColumns column) and a search
57
- 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
58
- colours ARE measured live: the parent segment muted at the same
59
- exact color-mix 63% sort-learn above already uses, the
60
- separator chevron and leaf segment both full fgPrimary (the
61
- generic menu-item svg rule below defaults every icon to
62
- fgMuted, so the separator needs its own override to read
63
- correctly here). */
64
- & .crisp-recordstable-menu-nesthead { display: flex; align-items: center; gap: var(--crisp-space-1); margin: var(--crisp-space-0-5); }
65
- & .crisp-recordstable-menu-nesthead .crisp-recordstable-menu-input { flex: 1; margin: 0; }
66
- & .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; }
67
- & .crisp-recordstable-menu-back:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
68
- & .crisp-recordstable-menu-item--expand { justify-content: space-between; }
69
- & .crisp-recordstable-menu-item-path { display: inline-flex; align-items: center; gap: 2px; min-width: 0; overflow: hidden; }
70
- & .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); }
71
- & .crisp-recordstable-menu-item-path-sep { flex-shrink: 0; color: var(--crisp-fg-primary); }
72
- & .crisp-recordstable-menu-item-path > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
73
- & .crisp-recordstable-menu--up { top: auto; bottom: calc(100% + var(--crisp-space-1)); transform-origin: bottom; }
74
- & .crisp-recordstable-menu--right { left: auto; right: 0; }
75
- & .crisp-recordstable-menu--fixed { position: fixed; top: auto; left: auto; z-index: 40; }
76
- & .crisp-recordstable-menu--hdr { min-width: 200px; }
77
- & .crisp-recordstable-menu--op { min-width: 0; }
78
- & .crisp-recordstable-menu-item--danger { color: var(--crisp-bg-danger); }
79
- & .crisp-recordstable-menu-item--danger svg { color: var(--crisp-bg-danger); }
80
- & .crisp-recordstable-menu-item--danger:hover { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
81
- & .crisp-recordstable-menu-check { margin-left: auto; color: var(--crisp-bg-brand-solid); }
82
- & .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; }
83
- & .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; }
84
- & .crisp-recordstable-fchip-field { font-weight: var(--crisp-weight-medium); }
85
- & .crisp-recordstable-fchip-op { color: var(--crisp-fg-muted); }
86
- & .crisp-recordstable-fchip-val { font-weight: var(--crisp-weight-medium); max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
87
- & .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; }
88
- & .crisp-recordstable-fchip-x:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 10%, transparent); color: var(--crisp-fg-primary); }
89
- & .crisp-recordstable-spacer { flex: 1; }
90
- & .crisp-recordstable-count { font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
91
- & .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; }
92
- & .crisp-recordstable-new:hover { background: var(--crisp-bg-brand-solid-pressed); }
93
-
94
- /* scroll frame + table */
95
- & .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; }
96
- & .crisp-recordstable-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
97
- & .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; }
98
- & .crisp-recordstable-scroll::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 34%, transparent); background-clip: padding-box; }
99
- & .crisp-recordstable-scroll::-webkit-scrollbar-corner { background: transparent; }
100
- & .crisp-recordstable-table { table-layout: fixed; border-collapse: separate; border-spacing: 0; }
101
- & .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; }
102
- & .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; }
103
- & .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; }
104
- & .crisp-recordstable-th-inner svg { flex-shrink: 0; color: color-mix(in srgb, var(--crisp-fg-primary) 45%, transparent); }
105
- & .crisp-recordstable-th-label { flex: 1; overflow: hidden; text-overflow: ellipsis; }
106
- & .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; }
107
- & .crisp-recordstable-th-rename:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
108
- & .crisp-recordstable-sort { flex-shrink: 0; opacity: 0.4; transition: opacity .12s; color: color-mix(in srgb, var(--crisp-fg-primary) 45%, transparent); }
109
- & .crisp-recordstable-th:hover .crisp-recordstable-sort, & .crisp-recordstable-th[data-sorted] .crisp-recordstable-sort { opacity: 1; }
110
- & .crisp-recordstable-th[data-sorted="desc"] .crisp-recordstable-sort svg { transform: rotate(180deg); }
111
-
112
- /* column resize handle */
113
- & .crisp-recordstable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
114
- & .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; }
115
- & .crisp-recordstable-resize:hover::after, & .crisp-recordstable-resize[data-resizing]::after { opacity: 1; }
116
-
117
- /* add-column header */
118
- & .crisp-recordstable-addcol { min-width: 44px; }
119
- & .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; }
120
- & .crisp-recordstable-addcol button:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
121
-
122
- /* cells */
123
- /* font-weight is explicit because the first cell of each row is a
124
- 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. */
125
- & .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; }
126
- & .crisp-recordstable-td--editable { cursor: text; }
127
- /* keyboard/click cell selection (spreadsheet-style) */
128
- & .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; }
129
- & .crisp-recordstable-scroll:focus { outline: none; }
130
- /* picker cells (status / date / reference) host a ghost control edge-to-edge */
131
- & .crisp-recordstable-td--picker { padding: 0; }
132
- & .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); }
133
- & .crisp-recordstable-td--picker .crisp-multiselect-trigger:hover { background: none; }
134
- & .crisp-recordstable-td--picker .crisp-multiselect-trigger[data-open] { box-shadow: inset 0 0 0 1.5px var(--crisp-bg-brand-solid); }
135
- /* tag cell: one compact clipped line of full chips, no chevron (opens the picker on click) */
136
- & .crisp-recordstable-td--picker .crisp-multiselect-chips { flex-wrap: nowrap; overflow: hidden; }
137
- & .crisp-recordstable-td--picker .crisp-multiselect-chips .crisp-recordstable-tag { flex-shrink: 0; max-width: none; }
138
- & .crisp-recordstable-td--picker .crisp-multiselect-chevron { display: none; }
139
- & .crisp-recordstable-td--picker .crisp-multiselect-placeholder { color: var(--crisp-fg-muted); }
140
- /* 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
141
- the create row, both sitting in the option's existing 8px gap, no new gap value invented). */
142
- & .crisp-recordstable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
143
- & .crisp-recordstable-refoption-avatar { flex-shrink: 0; }
144
- & .crisp-recordstable-refoption--create { color: var(--crisp-fg-muted); }
145
- & .crisp-recordstable-status { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
146
- & .crisp-recordstable-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); }
147
- & .crisp-recordstable-cell { display: flex; align-items: center; gap: var(--crisp-space-2); height: 100%; min-width: 0; }
148
- & .crisp-recordstable-cell--num { justify-content: flex-end; font-variant-numeric: tabular-nums; }
149
- & .crisp-recordstable-cell--tags { gap: var(--crisp-space-1); }
150
- & .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); }
151
- & .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); }
152
- & .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); }
153
- & .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); }
154
- & .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); }
155
- & .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); }
156
- & .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); }
157
- & .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); }
158
- & .crisp-recordstable-muted { color: var(--crisp-fg-muted); }
159
- & .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; }
160
- & .crisp-recordstable-entity:hover .crisp-recordstable-entity-name { text-decoration: underline; }
161
- & .crisp-recordstable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
162
- & 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%; }
163
- & .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); }
164
- & .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; }
165
- & .crisp-recordstable-open:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
166
- & tbody tr:hover .crisp-recordstable-open { opacity: 1; }
167
- & .crisp-recordstable-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
168
- & .crisp-recordstable-link:hover { text-decoration: underline; }
169
- /* inline edit input */
170
- & .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; }
171
- & .crisp-recordstable-edit--num { text-align: end; font-variant-numeric: tabular-nums; }
172
- /* selection / hover / sticky */
173
- & .crisp-recordstable-col-check { width: 40px; min-width: 40px; padding: 0; }
174
- & .crisp-recordstable-check-inner { display: flex; align-items: center; justify-content: center; height: 100%; }
175
- & .crisp-recordstable-col-check, & .crisp-recordstable-col-entity { position: sticky; z-index: 3; background: var(--crisp-bg-surface); }
176
- /* 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. */
177
- & .crisp-recordstable-col-check { inset-inline-start: 0; }
178
- & .crisp-recordstable-col-entity { inset-inline-start: 40px; clip-path: inset(0 -38px 0 0); transition: box-shadow .2s ease; }
179
- [dir="rtl"] & .crisp-recordstable-col-entity { clip-path: inset(0 0 0 -38px); }
180
- & thead .crisp-recordstable-col-check, & thead .crisp-recordstable-col-entity { z-index: 4; }
181
- /* soft shadow on the sticky column edge once the grid is scrolled horizontally */
182
- & .crisp-recordstable-scroll[data-scrolled] .crisp-recordstable-col-entity { box-shadow: 6px 0 16px 4px rgba(0,0,0,0.12); }
183
- /* box-shadow has no logical form either: the bleed follows the scrolled content, which is the opposite side under RTL. */
184
- [dir="rtl"] & .crisp-recordstable-scroll[data-scrolled] .crisp-recordstable-col-entity { box-shadow: -6px 0 16px 4px rgba(0,0,0,0.12); }
185
- /* 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. */
186
- & .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); }
187
- & tbody tr:hover .crisp-recordstable-td { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); }
188
- & tbody tr[data-selected] .crisp-recordstable-td { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
189
- & 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))); }
190
- & .crisp-recordstable-th:last-child, & .crisp-recordstable-td:last-child { border-inline-end: none; }
191
- /* add-row footer */
192
- & .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; }
193
- & .crisp-recordstable-addrow:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
194
- /* zero-row empty state — centred in the body region (headers + summary stay) */
195
- & .crisp-recordstable-empty { display: flex; align-items: center; justify-content: center; min-height: 320px; padding: var(--crisp-space-8) var(--crisp-space-4); }
196
- /* summary footer — per-column aggregation, sticky to the bottom */
197
- & .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); }
198
- & tfoot .crisp-recordstable-col-check, & tfoot .crisp-recordstable-col-entity { position: sticky; bottom: 0; z-index: 3; }
199
- & .crisp-recordstable-sum:last-child { border-inline-end: none; }
200
- & .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; }
201
- & .crisp-recordstable-sum-btn:hover { background: var(--crisp-bg-raised); }
202
- & .crisp-recordstable-sum-label { color: color-mix(in srgb, var(--crisp-fg-primary) 45%, transparent); text-transform: capitalize; }
203
- & .crisp-recordstable-sum-value { color: var(--crisp-fg-primary); font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; }
204
- & .crisp-recordstable-sumgroup { width: 100%; height: 100%; }
205
- & .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); }
206
- & .crisp-recordstable-sum-btn[data-empty] { opacity: 0; justify-content: flex-start; }
207
- & .crisp-recordstable-sum-btn[data-empty]:hover { opacity: 1; }
208
- /* bulk action bar lives in its own component now — see components/bulkbar */
209
- & .crisp-recordstable-bulk-sep { width: 1px; height: 20px; margin: 0 var(--crisp-space-0-5); background: var(--crisp-border-subtle); }
210
- & .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; }
211
- & .crisp-recordstable-bulk-close:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
212
- /* announced, never drawn */
213
- & .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; }
214
- [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
215
- }
216
-
217
- @media (prefers-reduced-motion: reduce) {
218
- .crisp-recordstable, .crisp-recordstable * {
219
- animation-duration: 0.01ms !important;
220
- animation-iteration-count: 1 !important;
221
- transition-duration: 0.01ms !important;
222
- }
223
- }
@@ -1,7 +0,0 @@
1
- import type { RecipeFn } from "../core/types";
2
-
3
- export interface RecordstableVariants {
4
-
5
- }
6
-
7
- export declare const recordstableRecipe: RecipeFn<{ }>;