@crisp-ui-kit/crisp 0.57.0 → 0.57.2

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 (293) hide show
  1. package/dist/accentpicker.css +5 -0
  2. package/dist/accentpicker.d.ts +7 -0
  3. package/dist/accentpicker.layered.css +7 -0
  4. package/dist/authshell.css +9 -4
  5. package/dist/authshell.layered.css +9 -4
  6. package/dist/avatar.css +1 -1
  7. package/dist/avatar.layered.css +1 -1
  8. package/dist/badge.css +5 -5
  9. package/dist/badge.layered.css +5 -5
  10. package/dist/board.css +28 -8
  11. package/dist/board.layered.css +28 -8
  12. package/dist/bottomsheet.css +26 -0
  13. package/dist/bottomsheet.d.ts +7 -0
  14. package/dist/bottomsheet.layered.css +28 -0
  15. package/dist/button.css +9 -8
  16. package/dist/button.layered.css +9 -8
  17. package/dist/checkbox.css +7 -3
  18. package/dist/checkbox.layered.css +7 -3
  19. package/dist/chip.css +15 -0
  20. package/dist/chip.d.ts +3 -1
  21. package/dist/chip.layered.css +15 -0
  22. package/dist/chipgroup.css +5 -0
  23. package/dist/chipgroup.d.ts +7 -0
  24. package/dist/chipgroup.layered.css +7 -0
  25. package/dist/cjs/components/accentpicker/accentpicker.js +66 -0
  26. package/dist/cjs/components/accentpicker/accentpicker.recipe.js +25 -0
  27. package/dist/cjs/components/accentpicker/index.js +7 -0
  28. package/dist/cjs/components/avatar/avatar.recipe.js +4 -1
  29. package/dist/cjs/components/badge/badge.recipe.js +38 -10
  30. package/dist/cjs/components/bottomsheet/bottomsheet.js +90 -0
  31. package/dist/cjs/components/bottomsheet/bottomsheet.recipe.js +77 -0
  32. package/dist/cjs/components/bottomsheet/index.js +9 -0
  33. package/dist/cjs/components/button/button.recipe.js +55 -21
  34. package/dist/cjs/components/checkbox/checkbox.recipe.js +61 -9
  35. package/dist/cjs/components/chip/chip.js +14 -2
  36. package/dist/cjs/components/chip/chip.recipe.js +69 -1
  37. package/dist/cjs/components/chipgroup/chipgroup.js +51 -0
  38. package/dist/cjs/components/chipgroup/chipgroup.recipe.js +36 -0
  39. package/dist/cjs/components/chipgroup/index.js +7 -0
  40. package/dist/cjs/components/datatable/datatable.js +5 -1
  41. package/dist/cjs/components/datatable/datatable.recipe.js +11 -2
  42. package/dist/cjs/components/dialog/dialog.recipe.js +21 -10
  43. package/dist/cjs/components/emptystate/emptystate.recipe.js +7 -2
  44. package/dist/cjs/components/fileupload/fileupload.js +21 -4
  45. package/dist/cjs/components/input/index.js +2 -1
  46. package/dist/cjs/components/input/input.js +33 -3
  47. package/dist/cjs/components/input/input.recipe.js +22 -7
  48. package/dist/cjs/components/menu/menu.recipe.js +21 -7
  49. package/dist/cjs/components/menusheet/index.js +9 -0
  50. package/dist/cjs/components/menusheet/menusheet.js +133 -0
  51. package/dist/cjs/components/menusheet/menusheet.recipe.js +103 -0
  52. package/dist/cjs/components/multiselect/multiselect.js +2 -2
  53. package/dist/cjs/components/multiselect/multiselect.recipe.js +45 -12
  54. package/dist/cjs/components/progress/progress.js +21 -4
  55. package/dist/cjs/components/progress/progress.recipe.js +30 -0
  56. package/dist/cjs/components/select/select.js +3 -3
  57. package/dist/cjs/components/select/select.recipe.js +63 -10
  58. package/dist/cjs/components/snackbar/index.js +8 -0
  59. package/dist/cjs/components/snackbar/snackbar.js +121 -0
  60. package/dist/cjs/components/snackbar/snackbar.recipe.js +71 -0
  61. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +7 -2
  62. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +37 -12
  63. package/dist/cjs/components/tabs/tabs.js +10 -4
  64. package/dist/cjs/components/tabs/tabs.recipe.js +23 -6
  65. package/dist/cjs/components/text/text.recipe.js +8 -7
  66. package/dist/cjs/components/textarea/textarea.js +3 -2
  67. package/dist/cjs/components/textarea/textarea.recipe.js +13 -9
  68. package/dist/cjs/components/textlink/textlink.recipe.js +6 -3
  69. package/dist/cjs/index.js +8 -0
  70. package/dist/cjs/product/authshell/authshell.recipe.js +5 -1
  71. package/dist/cjs/product/command/command.recipe.js +4 -1
  72. package/dist/cjs/product/comments/comments.js +7 -2
  73. package/dist/cjs/product/comments/comments.recipe.js +25 -7
  74. package/dist/cjs/product/memberssettings/index.js +6 -0
  75. package/dist/cjs/product/memberssettings/memberssettings.js +99 -0
  76. package/dist/cjs/product/memberssettings/memberssettings.recipe.js +83 -0
  77. package/dist/cjs/product/notificationssettings/index.js +5 -0
  78. package/dist/cjs/product/notificationssettings/notificationssettings.js +19 -0
  79. package/dist/cjs/product/notificationssettings/notificationssettings.recipe.js +44 -0
  80. package/dist/cjs/product/recordpage/recordpage.js +101 -9
  81. package/dist/cjs/product/recordpage/recordpage.recipe.js +43 -6
  82. package/dist/cjs/product/workspacegeneralsettings/index.js +5 -0
  83. package/dist/cjs/product/workspacegeneralsettings/workspacegeneralsettings.js +21 -0
  84. package/dist/cjs/product/workspacegeneralsettings/workspacegeneralsettings.recipe.js +51 -0
  85. package/dist/cjs/tokens/contract.js +1 -0
  86. package/dist/cjs/tokens/elevation.js +8 -5
  87. package/dist/cjs/tokens/palette.js +107 -33
  88. package/dist/cjs/tokens/scale.js +45 -2
  89. package/dist/cjs/tokens/semantic.js +26 -0
  90. package/dist/columncalc.css +15 -7
  91. package/dist/columncalc.layered.css +15 -7
  92. package/dist/command.css +23 -19
  93. package/dist/command.layered.css +23 -19
  94. package/dist/comments.css +28 -19
  95. package/dist/comments.d.ts +2 -2
  96. package/dist/comments.layered.css +28 -19
  97. package/dist/datatable.css +161 -67
  98. package/dist/datatable.layered.css +161 -67
  99. package/dist/datepicker.css +13 -41
  100. package/dist/datepicker.layered.css +13 -41
  101. package/dist/datepickerfield.css +4 -1
  102. package/dist/datepickerfield.layered.css +4 -1
  103. package/dist/dialog.css +12 -7
  104. package/dist/dialog.layered.css +12 -7
  105. package/dist/emptystate.css +5 -3
  106. package/dist/emptystate.layered.css +5 -3
  107. package/dist/esm/components/accentpicker/accentpicker.js +30 -0
  108. package/dist/esm/components/accentpicker/accentpicker.recipe.js +22 -0
  109. package/dist/esm/components/accentpicker/index.js +2 -0
  110. package/dist/esm/components/avatar/avatar.recipe.js +4 -1
  111. package/dist/esm/components/badge/badge.recipe.js +38 -10
  112. package/dist/esm/components/bottomsheet/bottomsheet.js +52 -0
  113. package/dist/esm/components/bottomsheet/bottomsheet.recipe.js +74 -0
  114. package/dist/esm/components/bottomsheet/index.js +2 -0
  115. package/dist/esm/components/button/button.recipe.js +56 -22
  116. package/dist/esm/components/checkbox/checkbox.recipe.js +61 -9
  117. package/dist/esm/components/chip/chip.js +14 -2
  118. package/dist/esm/components/chip/chip.recipe.js +69 -1
  119. package/dist/esm/components/chipgroup/chipgroup.js +15 -0
  120. package/dist/esm/components/chipgroup/chipgroup.recipe.js +33 -0
  121. package/dist/esm/components/chipgroup/index.js +2 -0
  122. package/dist/esm/components/datatable/datatable.js +5 -1
  123. package/dist/esm/components/datatable/datatable.recipe.js +11 -2
  124. package/dist/esm/components/dialog/dialog.recipe.js +22 -11
  125. package/dist/esm/components/emptystate/emptystate.recipe.js +7 -2
  126. package/dist/esm/components/fileupload/fileupload.js +20 -5
  127. package/dist/esm/components/input/index.js +1 -1
  128. package/dist/esm/components/input/input.js +33 -3
  129. package/dist/esm/components/input/input.recipe.js +23 -8
  130. package/dist/esm/components/menu/menu.recipe.js +21 -7
  131. package/dist/esm/components/menusheet/index.js +2 -0
  132. package/dist/esm/components/menusheet/menusheet.js +95 -0
  133. package/dist/esm/components/menusheet/menusheet.recipe.js +100 -0
  134. package/dist/esm/components/multiselect/multiselect.js +2 -2
  135. package/dist/esm/components/multiselect/multiselect.recipe.js +45 -12
  136. package/dist/esm/components/progress/progress.js +22 -5
  137. package/dist/esm/components/progress/progress.recipe.js +30 -0
  138. package/dist/esm/components/select/select.js +3 -3
  139. package/dist/esm/components/select/select.recipe.js +63 -10
  140. package/dist/esm/components/snackbar/index.js +2 -0
  141. package/dist/esm/components/snackbar/snackbar.js +84 -0
  142. package/dist/esm/components/snackbar/snackbar.recipe.js +68 -0
  143. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +7 -2
  144. package/dist/esm/components/tabletoolbar/tabletoolbar.js +37 -12
  145. package/dist/esm/components/tabs/tabs.js +10 -4
  146. package/dist/esm/components/tabs/tabs.recipe.js +23 -6
  147. package/dist/esm/components/text/text.recipe.js +8 -7
  148. package/dist/esm/components/textarea/textarea.js +3 -2
  149. package/dist/esm/components/textarea/textarea.recipe.js +14 -10
  150. package/dist/esm/components/textlink/textlink.recipe.js +6 -3
  151. package/dist/esm/index.js +8 -0
  152. package/dist/esm/product/authshell/authshell.recipe.js +5 -1
  153. package/dist/esm/product/command/command.recipe.js +4 -1
  154. package/dist/esm/product/comments/comments.js +7 -2
  155. package/dist/esm/product/comments/comments.recipe.js +25 -7
  156. package/dist/esm/product/memberssettings/index.js +1 -0
  157. package/dist/esm/product/memberssettings/memberssettings.js +62 -0
  158. package/dist/esm/product/memberssettings/memberssettings.recipe.js +80 -0
  159. package/dist/esm/product/notificationssettings/index.js +1 -0
  160. package/dist/esm/product/notificationssettings/notificationssettings.js +16 -0
  161. package/dist/esm/product/notificationssettings/notificationssettings.recipe.js +41 -0
  162. package/dist/esm/product/recordpage/recordpage.js +102 -10
  163. package/dist/esm/product/recordpage/recordpage.recipe.js +43 -6
  164. package/dist/esm/product/workspacegeneralsettings/index.js +1 -0
  165. package/dist/esm/product/workspacegeneralsettings/workspacegeneralsettings.js +18 -0
  166. package/dist/esm/product/workspacegeneralsettings/workspacegeneralsettings.recipe.js +48 -0
  167. package/dist/esm/tokens/contract.js +2 -1
  168. package/dist/esm/tokens/elevation.js +8 -5
  169. package/dist/esm/tokens/palette.js +107 -33
  170. package/dist/esm/tokens/scale.js +44 -1
  171. package/dist/esm/tokens/semantic.js +26 -0
  172. package/dist/fileupload.css +3 -1
  173. package/dist/fileupload.layered.css +3 -1
  174. package/dist/input.css +11 -2
  175. package/dist/input.layered.css +11 -2
  176. package/dist/listpage.css +3 -1
  177. package/dist/listpage.layered.css +3 -1
  178. package/dist/memberssettings.css +67 -0
  179. package/dist/memberssettings.d.ts +7 -0
  180. package/dist/memberssettings.layered.css +69 -0
  181. package/dist/menu.css +2 -2
  182. package/dist/menu.layered.css +2 -2
  183. package/dist/menusheet.css +36 -0
  184. package/dist/menusheet.d.ts +7 -0
  185. package/dist/menusheet.layered.css +38 -0
  186. package/dist/multiselect.css +17 -6
  187. package/dist/multiselect.d.ts +2 -2
  188. package/dist/multiselect.layered.css +17 -6
  189. package/dist/native/tokens.d.ts +2 -1
  190. package/dist/native/tokens.js +53 -25
  191. package/dist/notificationssettings.css +22 -0
  192. package/dist/notificationssettings.d.ts +7 -0
  193. package/dist/notificationssettings.layered.css +24 -0
  194. package/dist/objectsettings.css +4 -4
  195. package/dist/objectsettings.layered.css +4 -4
  196. package/dist/progress.css +3 -0
  197. package/dist/progress.layered.css +3 -0
  198. package/dist/recordpage.css +67 -23
  199. package/dist/recordpage.layered.css +67 -23
  200. package/dist/recordpanel.css +35 -16
  201. package/dist/recordpanel.layered.css +35 -16
  202. package/dist/resizablepanel.css +8 -5
  203. package/dist/resizablepanel.layered.css +8 -5
  204. package/dist/richtext.css +24 -8
  205. package/dist/richtext.layered.css +24 -8
  206. package/dist/savedviews.css +9 -2
  207. package/dist/savedviews.layered.css +9 -2
  208. package/dist/select.css +47 -7
  209. package/dist/select.d.ts +2 -2
  210. package/dist/select.layered.css +47 -7
  211. package/dist/settingsshell.css +2 -3
  212. package/dist/settingsshell.layered.css +2 -3
  213. package/dist/slider.css +6 -27
  214. package/dist/slider.layered.css +6 -27
  215. package/dist/snackbar.css +24 -0
  216. package/dist/snackbar.d.ts +7 -0
  217. package/dist/snackbar.layered.css +26 -0
  218. package/dist/statcard.css +3 -1
  219. package/dist/statcard.layered.css +3 -1
  220. package/dist/styles.css +927 -365
  221. package/dist/styles.layered.css +927 -365
  222. package/dist/table.css +28 -10
  223. package/dist/table.layered.css +28 -10
  224. package/dist/tabletoolbar.css +35 -24
  225. package/dist/tabletoolbar.layered.css +35 -24
  226. package/dist/tabs.css +18 -4
  227. package/dist/tabs.layered.css +18 -4
  228. package/dist/text.css +1 -1
  229. package/dist/text.layered.css +1 -1
  230. package/dist/textarea.css +4 -7
  231. package/dist/textarea.layered.css +4 -7
  232. package/dist/textlink.css +2 -2
  233. package/dist/textlink.layered.css +2 -2
  234. package/dist/themepicker.css +2 -1
  235. package/dist/themepicker.layered.css +2 -1
  236. package/dist/tokens.css +31 -13
  237. package/dist/tokens.json +53 -25
  238. package/dist/types/components/accentpicker/accentpicker.d.ts +23 -0
  239. package/dist/types/components/accentpicker/accentpicker.recipe.d.ts +3 -0
  240. package/dist/types/components/accentpicker/index.d.ts +2 -0
  241. package/dist/types/components/bottomsheet/bottomsheet.d.ts +35 -0
  242. package/dist/types/components/bottomsheet/bottomsheet.recipe.d.ts +3 -0
  243. package/dist/types/components/bottomsheet/index.d.ts +2 -0
  244. package/dist/types/components/button/button.recipe.d.ts +13 -8
  245. package/dist/types/components/checkbox/checkbox.recipe.d.ts +28 -5
  246. package/dist/types/components/chip/chip.d.ts +22 -0
  247. package/dist/types/components/chip/chip.recipe.d.ts +48 -0
  248. package/dist/types/components/chipgroup/chipgroup.d.ts +14 -0
  249. package/dist/types/components/chipgroup/chipgroup.recipe.d.ts +5 -0
  250. package/dist/types/components/chipgroup/index.d.ts +2 -0
  251. package/dist/types/components/datatable/datatable.d.ts +19 -0
  252. package/dist/types/components/fileupload/fileupload.d.ts +26 -0
  253. package/dist/types/components/input/index.d.ts +1 -1
  254. package/dist/types/components/input/input.d.ts +84 -0
  255. package/dist/types/components/menusheet/index.d.ts +2 -0
  256. package/dist/types/components/menusheet/menusheet.d.ts +42 -0
  257. package/dist/types/components/menusheet/menusheet.recipe.d.ts +3 -0
  258. package/dist/types/components/multiselect/multiselect.d.ts +4 -3
  259. package/dist/types/components/multiselect/multiselect.recipe.d.ts +3 -0
  260. package/dist/types/components/progress/progress.d.ts +16 -4
  261. package/dist/types/components/progress/progress.recipe.d.ts +22 -0
  262. package/dist/types/components/select/select.d.ts +6 -5
  263. package/dist/types/components/select/select.recipe.d.ts +3 -0
  264. package/dist/types/components/snackbar/index.d.ts +2 -0
  265. package/dist/types/components/snackbar/snackbar.d.ts +36 -0
  266. package/dist/types/components/snackbar/snackbar.recipe.d.ts +5 -0
  267. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +28 -1
  268. package/dist/types/components/tabs/tabs.d.ts +11 -1
  269. package/dist/types/components/textarea/textarea.d.ts +16 -0
  270. package/dist/types/index.d.ts +8 -0
  271. package/dist/types/product/comments/comments.d.ts +9 -3
  272. package/dist/types/product/comments/comments.recipe.d.ts +12 -2
  273. package/dist/types/product/memberssettings/index.d.ts +1 -0
  274. package/dist/types/product/memberssettings/memberssettings.d.ts +85 -0
  275. package/dist/types/product/memberssettings/memberssettings.recipe.d.ts +3 -0
  276. package/dist/types/product/notificationssettings/index.d.ts +1 -0
  277. package/dist/types/product/notificationssettings/notificationssettings.d.ts +37 -0
  278. package/dist/types/product/notificationssettings/notificationssettings.recipe.d.ts +3 -0
  279. package/dist/types/product/recordpage/recordpage.d.ts +89 -2
  280. package/dist/types/product/workspacegeneralsettings/index.d.ts +1 -0
  281. package/dist/types/product/workspacegeneralsettings/workspacegeneralsettings.d.ts +48 -0
  282. package/dist/types/product/workspacegeneralsettings/workspacegeneralsettings.recipe.d.ts +3 -0
  283. package/dist/types/tokens/contract.d.ts +7 -1
  284. package/dist/types/tokens/elevation.d.ts +8 -5
  285. package/dist/types/tokens/palette.d.ts +28 -12
  286. package/dist/types/tokens/scale.d.ts +12 -1
  287. package/dist/types/tokens/semantic.d.ts +17 -0
  288. package/dist/wheelpicker.css +4 -2
  289. package/dist/wheelpicker.layered.css +4 -2
  290. package/dist/workspacegeneralsettings.css +30 -0
  291. package/dist/workspacegeneralsettings.d.ts +7 -0
  292. package/dist/workspacegeneralsettings.layered.css +32 -0
  293. package/package.json +41 -1
@@ -0,0 +1,68 @@
1
+ import { defineRecipe, resolveRecipe } from "../../core/index.js";
2
+ import { contract as c } from "../../tokens/index.js";
3
+ // A mobile-pattern snackbar — daangn/seed-design's
4
+ // packages/rootage/components/snackbar.yaml (dev@e1c486b904297f7b6a8f17b8523f732c37015d60),
5
+ // not Attio (no such component lives there; crisp#911). A single dark
6
+ // `bgNeutralInverted` pill, bottom-anchored, holding an optional tone-coloured
7
+ // prefix icon, a message, and an optional bold action. Every numeric value
8
+ // below cites its yaml line; sizes/radii already equal existing crisp tokens
9
+ // (crisp's space/radius scale was itself derived 2px-granular, same as
10
+ // seed's `dimension`/`radius` scales, so no new tokens were needed).
11
+ //
12
+ // Deviation from the spec's literal `actionButton.color: $color.fg.brand`
13
+ // (snackbar.yaml:128, seed's carrot-600 `#ff6600`): crisp's OWN brand token
14
+ // is TechPartners' verified blue (`bgBrandSolid`, `#0298d9`), not seed's
15
+ // orange (the identity migration takes seed's STRUCTURE, crisp's own colour
16
+ // -- `~/grc-parity/seed-identity-spec.md`). Computed contrast of
17
+ // `bgBrandSolid` on `bgNeutralInverted` (light theme: `#0298d9` on `#2a3038`)
18
+ // is 4.11:1 -- below AA's 4.5:1 for 14px text (bold 14px does not clear the
19
+ // large-text 3:1 threshold, which needs 18.66px). crisp has no brand step
20
+ // bright enough on this dark surface yet, so the action reads in
21
+ // `fgOnNeutralInverted` (the same token the message already uses) at bold
22
+ // weight instead -- distinguished by weight, not colour, until a dedicated
23
+ // token is measured.
24
+ const config = defineRecipe({
25
+ name: "snackbar",
26
+ base: `
27
+ & .crisp-snackbar-region {
28
+ position: fixed; z-index: 80; inset-inline: 0; bottom: 0;
29
+ display: flex; justify-content: center;
30
+ padding: ${c.space["2"]}; /* snackbar.yaml:95-96 region.paddingX/paddingY = $dimension.x2 (8px) */
31
+ pointer-events: none;
32
+ }
33
+ & .crisp-snackbar {
34
+ pointer-events: auto; box-sizing: border-box;
35
+ display: flex; align-items: center; gap: ${c.space["2.5"]}; /* snackbar.yaml:116 content.gap = $dimension.x2_5 (10px), reused as the icon+message-to-action gap */
36
+ min-height: 44px; max-width: 464px; /* snackbar.yaml:102-103 */
37
+ width: max-content;
38
+ padding: ${c.space["2.5"]}; /* snackbar.yaml:104-105 root.paddingX/paddingY = $dimension.x2_5 (10px) */
39
+ border-radius: ${c.radius.md}; /* snackbar.yaml:101 root.cornerRadius = $radius.r2 (8px) */
40
+ background: ${c.bgNeutralInverted}; /* snackbar.yaml:100 root.color = $color.bg.neutral-inverted */
41
+ font-family: ${c.font.sans};
42
+ opacity: 1; transform: scale(1);
43
+ transition: opacity 150ms cubic-bezier(0,0,.15,1), transform 150ms cubic-bezier(0,0,.15,1); /* snackbar.yaml:108-109 enterDuration/enterTimingFunction = $duration.d3 (150ms) / $timing-function.enter */
44
+ @starting-style { opacity: 0; transform: scale(0.8); } /* snackbar.yaml:106-107 enterOpacity/enterScale */
45
+ }
46
+ & .crisp-snackbar-content { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: ${c.space["0.5"]}; } /* snackbar.yaml:124 prefixIcon.paddingRight = $dimension.x0_5 (2px), used as the icon-to-message gap */
47
+ & .crisp-snackbar-icon { flex-shrink: 0; display: inline-flex; }
48
+ & .crisp-snackbar[data-variant="positive"] .crisp-snackbar-icon { color: ${c.bgSuccess}; } /* snackbar.yaml:138-139 variant=positive prefixIcon.color = $color.fg.positive */
49
+ & .crisp-snackbar[data-variant="critical"] .crisp-snackbar-icon { color: ${c.bgDanger}; } /* snackbar.yaml:142-143 variant=critical prefixIcon.color = $color.fg.critical */
50
+ & .crisp-snackbar-message { flex: 1 1 auto; min-width: 0; color: ${c.fgOnNeutralInverted}; font-size: ${c.text.lg}; line-height: 19px; font-weight: ${c.weight.regular}; } /* snackbar.yaml:117-121: message.color = $color.fg.neutral-inverted, fontSize/lineHeight = $font-size.t4/$line-height.t4 (14px/19px), fontWeight = regular */
51
+ & .crisp-snackbar-action {
52
+ flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
53
+ min-height: 44px; padding: 0 ${c.space["2"]}; margin: -12px calc(-1 * ${c.space["2"]}); /* snackbar.yaml:126-127 targetPaddingX/targetMinHeight = $dimension.x2 (8px, doubled here as horizontal padding)/44px -- a larger invisible tap target than the pill's own 44px content row, via negative margin so it doesn't grow the pill visually */
54
+ border: none; border-radius: ${c.radius.md}; background: none;
55
+ color: ${c.fgOnNeutralInverted}; font-family: inherit; font-size: ${c.text.lg}; line-height: 19px; font-weight: ${c.weight.bold}; /* fontSize/lineHeight = t4/line-height.t4; fontWeight = bold (snackbar.yaml:129-131); colour deviation documented above */
56
+ cursor: pointer;
57
+ transition: transform 150ms cubic-bezier(0,0,.15,1);
58
+ }
59
+ & .crisp-snackbar-action:active { transform: scale(0.98); } /* snackbar.yaml:133-134 pressed.actionButton.scaleScope = self */
60
+ `,
61
+ variants: {},
62
+ defaultVariants: {},
63
+ compoundVariants: [],
64
+ });
65
+ export const snackbarRecipe = Object.assign(resolveRecipe(config), {
66
+ config,
67
+ });
68
+ export default config;
@@ -175,5 +175,10 @@ export const toolbarChromeBody = (p) => ` & .${CLASS_PREFIX}${p}-menu-head {
175
175
  & .${CLASS_PREFIX}${p}-fchip-x:hover { background: color-mix(in srgb, ${c.fgPrimary} 10%, transparent); color: ${c.fgPrimary}; }
176
176
  & .${CLASS_PREFIX}${p}-spacer { flex: 1; }
177
177
  & .${CLASS_PREFIX}${p}-count { font-size: ${c.text.md}; color: ${c.fgMuted}; }
178
- & .${CLASS_PREFIX}${p}-new { display: inline-flex; align-items: center; gap: ${c.space["1"]}; height: ${c.size.row}; padding: 0 ${c.space["2.5"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgBrandSolid}; color: ${c.fgOnBrand}; font-family: inherit; font-size: ${c.text.md}; font-weight: ${c.weight.medium}; cursor: pointer; transition: background-color .12s; }
179
- & .${CLASS_PREFIX}${p}-new:hover { background: ${c.bgBrandSolidPressed}; }`;
178
+ /* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
179
+ the accent (#0298d9) only clears 3.23:1 with white; the darker sibling
180
+ clears 4.5:1+, same fix already applied to Button/Badge/Avatar (#897).
181
+ Hover is a brightness filter on the new fill, not bgBrandSolidPressed
182
+ (that token stays tuned for the lighter accent's own consumers). */
183
+ & .${CLASS_PREFIX}${p}-new { display: inline-flex; align-items: center; gap: ${c.space["1"]}; height: ${c.size.row}; padding: 0 ${c.space["2.5"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgBrandContrast}; color: ${c.fgOnBrand}; font-family: inherit; font-size: ${c.text.md}; font-weight: ${c.weight.medium}; cursor: pointer; transition: filter .12s; }
184
+ & .${CLASS_PREFIX}${p}-new:hover { filter: brightness(0.96); }`;
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
2
2
  import { ArrowDownWideNarrow, ArrowUpDown, ArrowUpNarrowWide, Check, ChevronDown, ChevronLeft, ChevronRight, CircleHelp, Download, GripVertical, ListFilter, MoreVertical, Plus, SlidersHorizontal, Trash2, Upload, X, } from "lucide-react";
3
3
  import * as React from "react";
4
4
  import { createPortal } from "react-dom";
5
- import { FloatingPortal, isInsideLaterFloatingLayer, placeFloating, registerFloatingLayer, useCrispMessage, useCrispMessages, useFocusAfterRemoval, } from "../../core/index.js";
5
+ import { FloatingPortal, isInsideLaterFloatingLayer, placeFloating, registerFloatingLayer, useCrispComponentDefaults, useCrispMessage, useCrispMessages, useFocusAfterRemoval, } from "../../core/index.js";
6
6
  import { DatePicker, } from "../datepicker/index.js";
7
7
  import { MenuCheckboxItem, useMenuKeyboard } from "../menu/index.js";
8
8
  import { Select } from "../select/index.js";
@@ -44,7 +44,7 @@ function ViewBarSlot({ target, children, }) {
44
44
  // file, not per call site, and three independently attributed tags for
45
45
  // the same name over-counts against that (it expects one per name).
46
46
  const RtlChevronRight = ({ size, className, }) => (_jsx(ChevronRight, { "data-rtl-flip": "", size: size, strokeWidth: 1.75, className: className }));
47
- const OP_LABELS = {
47
+ export const toolbarOperatorLabels = {
48
48
  contains: "contains",
49
49
  doesnotcontain: "does not contain",
50
50
  startswith: "starts with",
@@ -65,6 +65,33 @@ const OP_LABELS = {
65
65
  before: "before",
66
66
  after: "after",
67
67
  };
68
+ /**
69
+ * crisp/#962: resolves each operator word through four layers, explicit
70
+ * prop winning, same "prop beats provider beats built-in" order as every
71
+ * other label in this file — `operatorLabels` prop, then a
72
+ * `CrispMessagesProvider`'s `componentDefaults.tableToolbar` (the new,
73
+ * discoverable, typed override), then the OLD whole-`CrispMessages`-
74
+ * dictionary-keyed-by-English-string lookup this file already had (kept
75
+ * so a consumer who already added entries there keeps working, unchanged),
76
+ * then the hardcoded English word. Not `useCrispMessage` in a loop over 13
77
+ * keys — that breaks the rules of hooks the same reason `useCrispMessages`
78
+ * itself exists (see that hook's own doc comment).
79
+ */
80
+ function useToolbarOperatorLabels(operatorLabels, crispMessages) {
81
+ const providerLabels = useCrispComponentDefaults("tableToolbar");
82
+ return React.useMemo(() => {
83
+ const resolved = {};
84
+ for (const op of Object.keys(toolbarOperatorLabels)) {
85
+ const builtIn = toolbarOperatorLabels[op];
86
+ resolved[op] =
87
+ operatorLabels?.[op] ??
88
+ providerLabels?.[op] ??
89
+ crispMessages[builtIn] ??
90
+ builtIn;
91
+ }
92
+ return resolved;
93
+ }, [operatorLabels, providerLabels, crispMessages]);
94
+ }
68
95
  // #619: order and label set match Attio's own text-filter operator picker
69
96
  // verbatim (measured live, per RecordsTable's own #578 port of the same
70
97
  // set: contains · does not contain · starts with · ends with · is · is not ·
@@ -329,7 +356,7 @@ export function applySorts(rows, sorts, get = (row, key) => row[key]) {
329
356
  * this (Sort: Due date/Assignee/Creation date; Filter: Assignee/Record/
330
357
  * Created by), which one shared list can't reproduce.
331
358
  */
332
- export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: filterColumnsProp, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel: sortLabelProp, filterLabel: filterLabelProp, reorderSortLabel: reorderSortLabelProp, noSortsLabel: noSortsLabelProp, addSortLabel: addSortLabelProp, learnSortingLabel: learnSortingLabelProp, selectConditionLabel: selectConditionLabelProp, sortFieldLabel: sortFieldLabelProp, sortDirectionLabel: sortDirectionLabelProp, removeSortLabel: removeSortLabelProp, filterFieldLabel: filterFieldLabelProp, filterFieldPlaceholder: filterFieldPlaceholderProp, filterValuePlaceholder: filterValuePlaceholderProp, filterOptionsLabel: filterOptionsLabelProp, deleteFilterLabel: deleteFilterLabelProp, ascendingLabel: ascendingLabelProp, descendingLabel: descendingLabelProp, sortTriggerLabel, filterTriggerLabel, inline, showSort = true, showViewSettings = true, viewSettingsRef, importExportRef, viewColumns: viewColumnsProp, shownKeys: shownKeysProp, defaultShownKeys, onShownKeysChange, viewSettingsLabel: viewSettingsLabelProp, reorderAttributeLabel: reorderAttributeLabelProp, hideAttributeLabel, addAttributeToViewLabel: addAttributeToViewLabelProp, searchAttributesPlaceholder: searchAttributesPlaceholderProp, importExportLabel: importExportLabelProp, onImportCsv, onExportCsv, onExportExcel, importExportHelpHref, importCsvLabel: importCsvLabelProp, exportCsvLabel: exportCsvLabelProp, exportExcelLabel: exportExcelLabelProp, learnImportExportLabel: learnImportExportLabelProp, backLabel: backLabelProp, nestedFieldPlaceholder, ...rest }) {
359
+ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: filterColumnsProp, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel: sortLabelProp, filterLabel: filterLabelProp, reorderSortLabel: reorderSortLabelProp, noSortsLabel: noSortsLabelProp, addSortLabel: addSortLabelProp, learnSortingLabel: learnSortingLabelProp, selectConditionLabel: selectConditionLabelProp, sortFieldLabel: sortFieldLabelProp, sortDirectionLabel: sortDirectionLabelProp, removeSortLabel: removeSortLabelProp, filterFieldLabel: filterFieldLabelProp, filterFieldPlaceholder: filterFieldPlaceholderProp, filterValuePlaceholder: filterValuePlaceholderProp, filterOptionsLabel: filterOptionsLabelProp, deleteFilterLabel: deleteFilterLabelProp, operatorLabels, ascendingLabel: ascendingLabelProp, descendingLabel: descendingLabelProp, sortTriggerLabel, filterTriggerLabel, inline, showSort = true, showViewSettings = true, viewSettingsRef, importExportRef, viewColumns: viewColumnsProp, shownKeys: shownKeysProp, defaultShownKeys, onShownKeysChange, viewSettingsLabel: viewSettingsLabelProp, reorderAttributeLabel: reorderAttributeLabelProp, hideAttributeLabel, addAttributeToViewLabel: addAttributeToViewLabelProp, searchAttributesPlaceholder: searchAttributesPlaceholderProp, importExportLabel: importExportLabelProp, onImportCsv, onExportCsv, onExportExcel, importExportHelpHref, importCsvLabel: importCsvLabelProp, exportCsvLabel: exportCsvLabelProp, exportExcelLabel: exportExcelLabelProp, learnImportExportLabel: learnImportExportLabelProp, backLabel: backLabelProp, nestedFieldPlaceholder, ...rest }) {
333
360
  // crisp/#471: an explicit prop always wins; missing, it reads the nearest
334
361
  // CrispMessagesProvider's translation of the English default below, or
335
362
  // that default itself with no provider — unchanged from before this
@@ -362,12 +389,12 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
362
389
  const exportExcelLabel = useCrispMessage(exportExcelLabelProp, "Export view as Excel");
363
390
  const learnImportExportLabel = useCrispMessage(learnImportExportLabelProp, "Learn about imports and exports");
364
391
  const backLabel = useCrispMessage(backLabelProp, "Back");
365
- // OP_LABELS' own words ("contains", "is", "is any of", …) have no prop of
366
- // their own — a whole lookup table, not one string — so they translate
367
- // straight from the provider's dictionary at the two spots they render
368
- // (#628, #673 below), same fallback order as every resolved constant
369
- // above.
370
392
  const crispMessages = useCrispMessages();
393
+ // crisp/#962: `operatorLabels` prop + `componentDefaults.tableToolbar`,
394
+ // layered over the old whole-dictionary lookup — see
395
+ // `useToolbarOperatorLabels`'s own doc comment for the full resolution
396
+ // order.
397
+ const opLabels = useToolbarOperatorLabels(operatorLabels, crispMessages);
371
398
  // Each side falls back to the shared `columns` independently — a caller
372
399
  // that only overrides one (Filter's own Record/Created by, say) still
373
400
  // gets `columns` on the other, unchanged from before either prop existed.
@@ -803,9 +830,7 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
803
830
  return (_jsxs("span", { className: "crisp-tabletoolbar-toolgroup", children: [_jsxs("span", { className: "crisp-tabletoolbar-fcond", "data-complete": complete ? "" : undefined, children: [_jsxs("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--field", "data-menu-trigger": true, "aria-haspopup": "dialog", "aria-expanded": fieldOpen, onClick: () => {
804
831
  setFieldPickerPath([]);
805
832
  setOpenMenu(fieldOpen ? null : `ffield:${cond.id}`);
806
- }, children: [col?.icon ? _jsx(col.icon, { size: 14, strokeWidth: 1.75 }) : null, colPath.length > 1 ? (_jsx("span", { className: "crisp-tabletoolbar-fcond-path", children: colPath.map((c, i) => (_jsxs(React.Fragment, { children: [i > 0 && (_jsx(RtlChevronRight, { size: 14, className: "crisp-tabletoolbar-fcond-path-sep" })), _jsx("span", { className: "crisp-tabletoolbar-fcond-path-seg", children: c.label })] }, c.key))) })) : (col?.label)] }), _jsx("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--op", "data-menu-trigger": true, "aria-haspopup": "menu", "aria-expanded": opOpen, onClick: () => setOpenMenu(opOpen ? null : `fop:${cond.id}`), children: cond.op
807
- ? (crispMessages[OP_LABELS[cond.op]] ?? OP_LABELS[cond.op])
808
- : selectConditionLabel }), showValueSeg && (_jsx("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--val", "data-menu-trigger": true, "aria-haspopup": "dialog", "aria-expanded": valOpen, onClick: () => setOpenMenu(valOpen ? null : `fval:${cond.id}`), children: cond.op === "isanyof"
833
+ }, children: [col?.icon ? _jsx(col.icon, { size: 14, strokeWidth: 1.75 }) : null, colPath.length > 1 ? (_jsx("span", { className: "crisp-tabletoolbar-fcond-path", children: colPath.map((c, i) => (_jsxs(React.Fragment, { children: [i > 0 && (_jsx(RtlChevronRight, { size: 14, className: "crisp-tabletoolbar-fcond-path-sep" })), _jsx("span", { className: "crisp-tabletoolbar-fcond-path-seg", children: c.label })] }, c.key))) })) : (col?.label)] }), _jsx("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--op", "data-menu-trigger": true, "aria-haspopup": "menu", "aria-expanded": opOpen, onClick: () => setOpenMenu(opOpen ? null : `fop:${cond.id}`), children: cond.op ? opLabels[cond.op] : selectConditionLabel }), showValueSeg && (_jsx("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--val", "data-menu-trigger": true, "aria-haspopup": "dialog", "aria-expanded": valOpen, onClick: () => setOpenMenu(valOpen ? null : `fval:${cond.id}`), children: cond.op === "isanyof"
809
834
  ? cond.values?.length
810
835
  ? isAnyOfSummary(cond.values)
811
836
  : filterValuePlaceholder
@@ -813,7 +838,7 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
813
838
  ? col?.type === "date"
814
839
  ? formatFilterDate(cond.value)
815
840
  : cond.value
816
- : filterValuePlaceholder })), _jsx("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--kebab", "data-menu-trigger": true, "aria-haspopup": "menu", "aria-expanded": kebabOpen, "aria-label": `${col?.label} ${filterOptionsLabel}`, onClick: () => setOpenMenu(kebabOpen ? null : `fkebab:${cond.id}`), children: _jsx(MoreVertical, { size: 13, strokeWidth: 2 }) })] }), kebabOpen && (_jsx("div", { ref: kebabMenuRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--right crisp-tabletoolbar-menu--hdr", role: "menu", "aria-label": `${col?.label} ${filterOptionsLabel}`, onKeyDown: onKebabKeyDown, children: _jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item crisp-tabletoolbar-menu-item--danger", onClick: () => removeFilter(cond.id), children: [_jsx(Trash2, { size: 16, strokeWidth: 1.75 }), deleteFilterLabel] }) })), fieldOpen && (_jsx("div", { className: "crisp-tabletoolbar-menu", role: "dialog", "aria-label": filterFieldLabel, children: nestedFieldPicker(filterColumns, (key) => changeFilterField(cond.id, key), filterFieldPlaceholder, filterFieldLabel) })), opOpen && (_jsx("div", { ref: opMenuRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--op", role: "menu", "aria-label": `${col?.label} ${crispMessages.condition ?? "condition"}`, onKeyDown: onOpKeyDown, children: opsFor(col).map((op) => (_jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item", "data-on": cond.op === op ? "" : undefined, onClick: () => pickOp(cond.id, op), children: [crispMessages[OP_LABELS[op]] ?? OP_LABELS[op], cond.op === op ? (_jsx(Check, { className: "crisp-tabletoolbar-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))) })), valOpen && showValueSeg && (_jsx("div", { className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--val", role: "dialog", "aria-label": `${col?.label} filter value`, children: cond.op === "isanyof" && col?.values ? (_jsxs(_Fragment, { children: [_jsx("input", { className: "crisp-tabletoolbar-menu-input", placeholder: filterValuePlaceholder, "aria-label": `${col?.label} filter values`,
841
+ : filterValuePlaceholder })), _jsx("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--kebab", "data-menu-trigger": true, "aria-haspopup": "menu", "aria-expanded": kebabOpen, "aria-label": `${col?.label} ${filterOptionsLabel}`, onClick: () => setOpenMenu(kebabOpen ? null : `fkebab:${cond.id}`), children: _jsx(MoreVertical, { size: 13, strokeWidth: 2 }) })] }), kebabOpen && (_jsx("div", { ref: kebabMenuRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--right crisp-tabletoolbar-menu--hdr", role: "menu", "aria-label": `${col?.label} ${filterOptionsLabel}`, onKeyDown: onKebabKeyDown, children: _jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item crisp-tabletoolbar-menu-item--danger", onClick: () => removeFilter(cond.id), children: [_jsx(Trash2, { size: 16, strokeWidth: 1.75 }), deleteFilterLabel] }) })), fieldOpen && (_jsx("div", { className: "crisp-tabletoolbar-menu", role: "dialog", "aria-label": filterFieldLabel, children: nestedFieldPicker(filterColumns, (key) => changeFilterField(cond.id, key), filterFieldPlaceholder, filterFieldLabel) })), opOpen && (_jsx("div", { ref: opMenuRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--op", role: "menu", "aria-label": `${col?.label} ${crispMessages.condition ?? "condition"}`, onKeyDown: onOpKeyDown, children: opsFor(col).map((op) => (_jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item", "data-on": cond.op === op ? "" : undefined, onClick: () => pickOp(cond.id, op), children: [opLabels[op], cond.op === op ? (_jsx(Check, { className: "crisp-tabletoolbar-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))) })), valOpen && showValueSeg && (_jsx("div", { className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--val", role: "dialog", "aria-label": `${col?.label} filter value`, children: cond.op === "isanyof" && col?.values ? (_jsxs(_Fragment, { children: [_jsx("input", { className: "crisp-tabletoolbar-menu-input", placeholder: filterValuePlaceholder, "aria-label": `${col?.label} filter values`,
817
842
  // biome-ignore lint/a11y/noAutofocus: focus the search field
818
843
  autoFocus: true, value: anyOfQuery, onChange: (e) => setAnyOfQuery(e.target.value) }), col.values
819
844
  .filter((v) => v
@@ -10,7 +10,7 @@ import { useTablistKeyboard } from "./tabs-keyboard.js";
10
10
  * Home/End jump); activation follows focus. Controlled via
11
11
  * `value`/`onValueChange`, or uncontrolled from `defaultValue`.
12
12
  */
13
- export function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, moreLabel = (n) => `+${n} more`, moreMenuLabel = "More tabs", className, fill, "aria-label": ariaLabel, ...rest }) {
13
+ export function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, moreLabel = (n) => `+${n} more`, moreMenuLabel = "More tabs", overflow = "collapse", className, fill, "aria-label": ariaLabel, ...rest }) {
14
14
  // Scoped to this instance: `tab-${value}` and `panel-${value}` are global,
15
15
  // so two tab bars sharing a value mint the same id twice and every
16
16
  // aria-labelledby resolves to the first one (#227).
@@ -57,6 +57,12 @@ export function Tabs({ tabs, value, defaultValue, onValueChange, leading, childr
57
57
  const [hiddenFrom, setHiddenFrom] = React.useState(null);
58
58
  const moreRef = React.useRef(null);
59
59
  React.useLayoutEffect(() => {
60
+ // crisp#908: "scroll" never collapses anything -- the row scrolls
61
+ // instead, so there is nothing to measure or hide.
62
+ if (overflow !== "collapse") {
63
+ setHiddenFrom(null);
64
+ return;
65
+ }
60
66
  const list = listRef.current;
61
67
  if (!list || tabs.length === 0)
62
68
  return;
@@ -97,7 +103,7 @@ export function Tabs({ tabs, value, defaultValue, onValueChange, leading, childr
97
103
  const ro = new ResizeObserver(recompute);
98
104
  ro.observe(list);
99
105
  return () => ro.disconnect();
100
- }, [tabs, active]);
106
+ }, [tabs, active, overflow]);
101
107
  const hiddenTabs = hiddenFrom == null ? [] : tabs.slice(hiddenFrom);
102
108
  const { onKeyDown, tabIndexFor } = useTablistKeyboard({
103
109
  tabs,
@@ -105,11 +111,11 @@ export function Tabs({ tabs, value, defaultValue, onValueChange, leading, childr
105
111
  select,
106
112
  listRef,
107
113
  });
108
- return (_jsxs("div", { className: tabsRecipe({ fill, className }), ...rest, children: [_jsxs("div", { ref: listRef, className: "crisp-tabs-list", role: "tablist", "aria-label": ariaLabel, onKeyDown: onKeyDown, children: [leading && _jsx("span", { className: "crisp-tabs-leading", children: leading }), tabs.map((t, i) => {
114
+ return (_jsxs("div", { className: tabsRecipe({ fill, className }), ...rest, children: [_jsxs("div", { ref: listRef, className: "crisp-tabs-list", "data-overflow": overflow, role: "tablist", "aria-label": ariaLabel, onKeyDown: onKeyDown, children: [leading && _jsx("span", { className: "crisp-tabs-leading", children: leading }), tabs.map((t, i) => {
109
115
  const selected = t.value === active;
110
116
  const overflowHidden = hiddenFrom != null && i >= hiddenFrom;
111
117
  return (_jsxs("button", { type: "button", role: "tab", id: tabId(t.value), "aria-selected": selected, "aria-controls": children && selected ? panelId(t.value) : undefined, tabIndex: overflowHidden ? -1 : tabIndexFor(t.value), "data-value": t.value, "data-selected": selected ? "" : undefined, "data-tabs-overflow-hidden": overflowHidden ? "" : undefined, disabled: t.disabled, className: "crisp-tabs-tab", onClick: () => select(t.value), children: [t.icon, _jsx("span", { className: "tabs-tab-label", children: t.label }), t.badge != null && (_jsx("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value));
112
- }), _jsx("span", { ref: moreRef, className: "crisp-tabs-more", "data-tabs-overflow-hidden": hiddenTabs.length === 0 ? "" : undefined, children: _jsx(Menu, { "aria-label": moreMenuLabel, trigger: _jsxs("button", { type: "button", className: "crisp-tabs-tab", children: [_jsx("span", { className: "tabs-tab-label", children: moreLabel(hiddenTabs.length) }), _jsx(ChevronDown, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" })] }), children: hiddenTabs.map((t) => (_jsxs(MenuItem, { icon: t.icon, onSelect: () => select(t.value), children: [t.label, t.badge != null && (_jsx("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value))) }) }), indicator && (_jsx("span", { className: "crisp-tabs-indicator", style: {
118
+ }), overflow === "collapse" && (_jsx("span", { ref: moreRef, className: "crisp-tabs-more", "data-tabs-overflow-hidden": hiddenTabs.length === 0 ? "" : undefined, children: _jsx(Menu, { "aria-label": moreMenuLabel, trigger: _jsxs("button", { type: "button", className: "crisp-tabs-tab", children: [_jsx("span", { className: "tabs-tab-label", children: moreLabel(hiddenTabs.length) }), _jsx(ChevronDown, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" })] }), children: hiddenTabs.map((t) => (_jsxs(MenuItem, { icon: t.icon, onSelect: () => select(t.value), children: [t.label, t.badge != null && (_jsx("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value))) }) })), indicator && (_jsx("span", { className: "crisp-tabs-indicator", style: {
113
119
  width: indicator.width,
114
120
  transform: `translateX(${indicator.left}px)`,
115
121
  } }))] }), children && (_jsx("div", { className: "crisp-tabs-panel", role: "tabpanel", id: panelId(active), "aria-labelledby": tabId(active),
@@ -5,10 +5,20 @@ import { contract as c } from "../../tokens/index.js";
5
5
  // baseline needs, reserved as bottom padding so it's real box height and not
6
6
  // paint that trailing content can sit on top of. The bar carries 12px inline
7
7
  // padding too, and draws its own full-width `::after` baseline in
8
- // borderSubtle at its bottom edge, spanning that padding. The active tab is
9
- // a light pill — bg #fbfbfb + inset ring borderSubtle, r8 — with a 1px ink
10
- // underline that sits on the baseline (painted above it) and slides along it;
11
- // inactive tabs are muted and fill to a subtle pill on hover. Each tab carries
8
+ // borderSubtle at its bottom edge, spanning that padding.
9
+ //
10
+ // your-moon/crisp#934: the selected tab used to be a light
11
+ // filled pill (bg #fbfbfb + inset ring borderSubtle, r8) with a 1px ink
12
+ // underline on top of it. Owner-directed: underline tabs, no fill pill --
13
+ // seed-design's own `tab.yaml` has no `root.color` property at all, at any
14
+ // state (selected/hover/pressed); selection is carried purely by the label
15
+ // darkening (muted -> ink, unchanged, already matched seed) plus the
16
+ // existing underline (already `fgPrimary`, "same colour as selected text,
17
+ // not brand" -- also already matched seed). Only the selected pill's own
18
+ // fill/ring is removed here; unselected tabs keep their existing subtle
19
+ // hover tint (a desktop hover affordance seed's spec doesn't need to
20
+ // consider one way or the other, and the owner didn't ask to remove).
21
+ // Inactive tabs are muted and fill to a subtle pill on hover. Each tab carries
12
22
  // a 16px leading icon and an optional count badge. Measured from the live
13
23
  // app: bar `padding: 0 12px 8px`, 28px pills, 4/8 padding, 6px icon-label
14
24
  // gap, 14/500 labels. The count badge (crisp#697, re-measured against a
@@ -45,11 +55,18 @@ const config = defineRecipe({
45
55
 
46
56
  & .crisp-tabs-list { position: relative; display: flex; align-items: center; gap: ${c.space["1"]}; box-sizing: content-box; height: ${c.size.row}; padding: 0 ${c.space["3"]} ${c.space["2"]}; flex-shrink: 0; }
47
57
  & .crisp-tabs-list::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: ${c.borderSubtle}; }
58
+ /* crisp#908: an alternative to the "+N more" collapse above (crisp#863) --
59
+ a consumer opts into a single scrolling row instead (Naver/Kakao's own
60
+ pattern, docs/study/korean/), never both at once. No new visual value:
61
+ reuses the same list gap/tab geometry, only adds overflow-x + snap. */
62
+ & .crisp-tabs-list[data-overflow="scroll"] { overflow-x: auto; flex-wrap: nowrap; scroll-snap-type: x mandatory; scrollbar-width: none; }
63
+ & .crisp-tabs-list[data-overflow="scroll"]::-webkit-scrollbar { display: none; }
64
+ & .crisp-tabs-list[data-overflow="scroll"] .crisp-tabs-tab { flex-shrink: 0; scroll-snap-align: start; }
48
65
  & .crisp-tabs-leading { display: inline-flex; align-items: center; margin-inline-end: ${c.space["1"]}; }
49
- & .crisp-tabs-tab { position: relative; display: inline-flex; align-items: center; gap: ${c.space["1.5"]}; height: 28px; margin: 0; padding: 0 ${c.space["2"]}; border: none; background: none; box-shadow: inset 0 0 0 1px transparent; border-radius: ${c.radius.md}; font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; letter-spacing: -0.14px; color: ${c.fgMuted}; cursor: pointer; white-space: nowrap; transition: color .12s ease, background-color .12s ease, box-shadow .12s ease; }
66
+ & .crisp-tabs-tab { position: relative; display: inline-flex; align-items: center; gap: ${c.space["1.5"]}; height: 28px; margin: 0; padding: 0 ${c.space["2"]}; border: none; background: none; border-radius: ${c.radius.md}; font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; letter-spacing: -0.14px; color: ${c.fgMuted}; cursor: pointer; white-space: nowrap; transition: color .12s ease, background-color .12s ease; }
50
67
  & .crisp-tabs-tab svg { flex-shrink: 0; opacity: 0.85; }
51
68
  & .crisp-tabs-tab:hover:not([data-selected]) { background: color-mix(in srgb, ${c.fgPrimary} 4%, transparent); color: ${c.fgPrimary}; }
52
- & .crisp-tabs-tab[data-selected] { color: ${c.fgPrimary}; background: ${c.bgSubtle}; box-shadow: inset 0 0 0 1px ${c.borderSubtle}; }
69
+ & .crisp-tabs-tab[data-selected] { color: ${c.fgPrimary}; }
53
70
  & .crisp-tabs-tab:focus-visible { outline: 2px solid ${c.bgBrandSolid}; outline-offset: 1px; }
54
71
  & .crisp-tabs-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 14px; height: 14px; padding: 0 ${c.space["1"]}; border-radius: 4px; background: color-mix(in srgb, ${c.fgPrimary} 4%, transparent); box-shadow: inset 0 0 0 1px ${c.borderSubtle}; font-size: 10px; line-height: 14px; font-weight: ${c.weight.medium}; color: ${c.fgNeutralSoft}; }
55
72
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: ${c.fgPrimary}; }
@@ -33,13 +33,14 @@ const spec = defineSpec({
33
33
  muted: { color: c.fgNeutralMuted },
34
34
  // #858: no new measurement -- both reuse tokens already measured
35
35
  // against Attio for the same colours in a different arrangement.
36
- // `brand` is `textlink`'s own reused brand-text colour (`bgBrandSolid`,
37
- // the same alias GRC's own `--color-fg-brand` resolves to). `danger`
38
- // is `fgDanger` ("solid critical/destructive TEXT... an overdue
39
- // due-date, a destructive menu item's label" -- see its own doc in
40
- // tokens/semantic.ts), not `fgDangerSoft` (that one's a soft Badge
41
- // pill's text, a different measured hex).
42
- brand: { color: c.bgBrandSolid },
36
+ // `brand` is `textlink`'s own reused brand-text colour, now
37
+ // `fgBrandContrast` (your-moon/crisp#897: the plain accent,
38
+ // `bgBrandSolid`, only clears 3.23:1 as text on a white page).
39
+ // `danger` is `fgDanger` ("solid critical/destructive TEXT... an
40
+ // overdue due-date, a destructive menu item's label" -- see its own
41
+ // doc in tokens/semantic.ts), not `fgDangerSoft` (that one's a soft
42
+ // Badge pill's text, a different measured hex).
43
+ brand: { color: c.fgBrandContrast },
43
44
  danger: { color: c.fgDanger },
44
45
  },
45
46
  },
@@ -1,12 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
+ import { Progress } from "../progress/index.js";
3
4
  import { textareaRecipe } from "./textarea.recipe.js";
4
5
  /**
5
6
  * A multiline text field — measured from settings. Forwards every native
6
7
  * `<textarea>` prop; pass `showCount` with `maxLength` to render a live
7
8
  * character counter (which turns red if the value exceeds the limit).
8
9
  */
9
- export const Textarea = React.forwardRef(({ showCount, maxLength, className, wrapperClassName, value, defaultValue, onChange,
10
+ export const Textarea = React.forwardRef(({ showCount, showProgress, maxLength, className, wrapperClassName, value, defaultValue, onChange,
10
11
  // `style` is pulled out and put on the ROOT, the way Input does it.
11
12
  // Everything else belongs on the <textarea> -- id, name, form
12
13
  // association, aria -- but `style` sizes the visual box, and the box is
@@ -22,6 +23,6 @@ style, ...props }, ref) => {
22
23
  if (value === undefined)
23
24
  setCount(e.target.value.length);
24
25
  onChange?.(e);
25
- }, ...props }) }), counted && (_jsxs("span", { className: "crisp-textarea-count", "data-over": len > (maxLength ?? 0) ? "" : undefined, "aria-hidden": "true", children: [len, " / ", maxLength] }))] }));
26
+ }, ...props }) }), counted && (_jsxs("span", { className: "crisp-textarea-count", "data-over": len > (maxLength ?? 0) ? "" : undefined, "aria-hidden": "true", children: [showProgress && (_jsx(Progress, { variant: "circular", value: len, max: maxLength ?? 0, className: "crisp-textarea-progress" })), len, " / ", maxLength] }))] }));
26
27
  });
27
28
  Textarea.displayName = "Textarea";
@@ -1,13 +1,16 @@
1
1
  import { defineRecipe, resolveRecipe } from "../../core/index.js";
2
- import { contract as c, elevation as e } from "../../tokens/index.js";
3
- // A multiline text field: an inset 1px ring (radius 8), 14px text, and an
2
+ import { contract as c } from "../../tokens/index.js";
3
+ // A multiline text field: a real 1px border (radius 8), 14px text, and an
4
4
  // optional character counter pinned bottom-right (12px, muted). Focus rings
5
5
  // the field brand.
6
6
  //
7
- // The ring is `elevation.textField` (#466), not `elevation.field` — this
8
- // comment already correctly described "an inset 1px ring", but the code
9
- // pulled in `field`'s own button-chrome shadow (a ring PLUS a glow and a
10
- // drop-shadow layer), which live Attio never carries on a real typed field.
7
+ // your-moon/crisp#920: the rest-state edge used to be `elevation.textField`,
8
+ // a `box-shadow` ring (#466's fix for a real typed field's 1px ring, not
9
+ // `field`'s button-chrome glow/drop-shadow layers) -- now a real `border`,
10
+ // matching Input's own #914/#910 technique and seed-design's `text-input.yaml`
11
+ // (an actual border that THICKENS on focus/invalid via the ::after overlay
12
+ // below, not a fixed-width ring that only recolors). `borderDefault` is
13
+ // seed's own `stroke.neutral-weak`, already the crisp token's exact value.
11
14
  //
12
15
  // The inset is seed-design's, not Attio's -- the second deliberate divergence
13
16
  // after the focus treatment. seed's text-input sets its inline inset to
@@ -24,10 +27,10 @@ const config = defineRecipe({
24
27
 
25
28
  & .crisp-textarea-field {
26
29
  box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical;
27
- margin: 0; padding: ${c.space["2.5"]} 14px; border: none; border-radius: ${c.radius.md};
28
- background: ${c.bgSurface}; box-shadow: ${e.textField};
30
+ margin: 0; padding: ${c.space["2.5"]} 14px; border-radius: ${c.radius.md};
31
+ background: ${c.bgSurface}; border: 1px solid ${c.borderDefault};
29
32
  font-family: inherit; font-size: ${c.text.lg}; line-height: 20px; color: ${c.fgPrimary};
30
- outline: none; transition: box-shadow .15s ease;
33
+ outline: none;
31
34
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
32
35
  }
33
36
  & .crisp-textarea-field::placeholder { color: ${c.fgMuted}; }
@@ -46,8 +49,9 @@ const config = defineRecipe({
46
49
  &[data-invalid] .crisp-textarea-wrap::after { border-color: ${c.bgDanger}; }
47
50
  &[data-invalid] .crisp-textarea-wrap:focus-within::after { border-color: ${c.bgDanger}; }
48
51
  &[data-counted] .crisp-textarea-field { padding-bottom: ${c.space["5"]}; }
49
- & .crisp-textarea-count { position: absolute; inset-inline-end: 14px; bottom: ${c.space["2.5"]}; font-size: ${c.text.sm}; color: ${c.fgMuted}; pointer-events: none; }
52
+ & .crisp-textarea-count { position: absolute; inset-inline-end: 14px; bottom: ${c.space["2.5"]}; display: flex; align-items: center; gap: ${c.space["1"]}; font-size: ${c.text.sm}; color: ${c.fgMuted}; pointer-events: none; }
50
53
  & .crisp-textarea-count[data-over] { color: ${c.bgDanger}; }
54
+ & .crisp-textarea-progress { flex-shrink: 0; }
51
55
  `,
52
56
  variants: {},
53
57
  defaultVariants: {},
@@ -18,11 +18,14 @@ const config = defineRecipe({
18
18
  variants: {
19
19
  tone: {
20
20
  // The bold, always-brand-coloured trailing link a Card's own `action`
21
- // slot uses ("View all →", "All registers →").
22
- brand: `font-size: ${c.text.sm}; font-weight: ${c.weight.medium}; color: ${c.bgBrandSolid};`,
21
+ // slot uses ("View all →", "All registers →"). your-moon/crisp#897:
22
+ // `fgBrandContrast`, not `bgBrandSolid` (#0298d9) directly -- as TEXT
23
+ // on a white page the accent itself only clears 3.23:1; the darker
24
+ // contrast-safe sibling clears 4.5:1+.
25
+ brand: `font-size: ${c.text.sm}; font-weight: ${c.weight.medium}; color: ${c.fgBrandContrast};`,
23
26
  // A quieter in-context reference (a sample record under a finding)
24
27
  // that only goes brand on hover.
25
- muted: `font-size: ${c.text.md}; font-weight: ${c.weight.regular}; color: ${c.fgMuted}; &:hover { color: ${c.bgBrandSolid}; }`,
28
+ muted: `font-size: ${c.text.md}; font-weight: ${c.weight.regular}; color: ${c.fgMuted}; &:hover { color: ${c.fgBrandContrast}; }`,
26
29
  },
27
30
  // crisp#874: an explicit, OPTIONAL size override -- no `defaultVariants`
28
31
  // entry below, so omitting `size` emits no `--size_*` class at all and
package/dist/esm/index.js CHANGED
@@ -2,11 +2,13 @@
2
2
  // component, the design tokens, and the recipe core. Consumers also import the
3
3
  // stylesheet once: import "@crisp-ui-kit/crisp/styles.css";
4
4
  // primitives (components/)
5
+ export * from "./components/accentpicker/index.js";
5
6
  export * from "./components/accordion/index.js";
6
7
  export * from "./components/avatar/index.js";
7
8
  export * from "./components/avatargroup/index.js";
8
9
  export * from "./components/badge/index.js";
9
10
  export * from "./components/barchart/index.js";
11
+ export * from "./components/bottomsheet/index.js";
10
12
  export * from "./components/breadcrumb/index.js";
11
13
  export * from "./components/bulkbar/index.js";
12
14
  export * from "./components/button/index.js";
@@ -14,6 +16,7 @@ export * from "./components/callout/index.js";
14
16
  export * from "./components/card/index.js";
15
17
  export * from "./components/checkbox/index.js";
16
18
  export * from "./components/chip/index.js";
19
+ export * from "./components/chipgroup/index.js";
17
20
  export * from "./components/columncalc/index.js";
18
21
  export * from "./components/composewindow/index.js";
19
22
  export * from "./components/contextmenu/index.js";
@@ -35,6 +38,7 @@ export * from "./components/kbd/index.js";
35
38
  export * from "./components/linechart/index.js";
36
39
  export * from "./components/mentionchip/index.js";
37
40
  export * from "./components/menu/index.js";
41
+ export * from "./components/menusheet/index.js";
38
42
  export * from "./components/middletruncate/index.js";
39
43
  export * from "./components/multiselect/index.js";
40
44
  export * from "./components/personchip/index.js";
@@ -48,6 +52,7 @@ export * from "./components/select/index.js";
48
52
  export * from "./components/skeleton/index.js";
49
53
  export * from "./components/skiplink/index.js";
50
54
  export * from "./components/slider/index.js";
55
+ export * from "./components/snackbar/index.js";
51
56
  export * from "./components/spinner/index.js";
52
57
  export * from "./components/stagebar/index.js";
53
58
  export * from "./components/statcard/index.js";
@@ -82,7 +87,9 @@ export * from "./product/headerbar/index.js";
82
87
  export * from "./product/importdialog/index.js";
83
88
  export * from "./product/inbox/index.js";
84
89
  export * from "./product/listpage/index.js";
90
+ export * from "./product/memberssettings/index.js";
85
91
  export * from "./product/navitem/index.js";
92
+ export * from "./product/notificationssettings/index.js";
86
93
  export * from "./product/objectsettings/index.js";
87
94
  export * from "./product/onboarding/index.js";
88
95
  export * from "./product/recordpage/index.js";
@@ -97,5 +104,6 @@ export * from "./product/tasklist/index.js";
97
104
  export * from "./product/themepicker/index.js";
98
105
  export * from "./product/trialbanner/index.js";
99
106
  export * from "./product/viewtoggle/index.js";
107
+ export * from "./product/workspacegeneralsettings/index.js";
100
108
  // design tokens + recipe core (for building custom components)
101
109
  export * from "./tokens/index.js";
@@ -40,7 +40,11 @@ const config = defineRecipe({
40
40
  & .crisp-authshell-progress-fill { height: 2px; background: ${c.fgPrimary}; border-radius: 4px; transition: width .2s; }
41
41
 
42
42
  & .crisp-authshell-logo { margin-top: ${c.space["8"]}; min-height: 24px; display: flex; align-items: center; gap: ${c.space["2.5"]}; }
43
- & .crisp-authshell-logo-badge { flex-shrink: 0; box-sizing: border-box; width: 32px; height: 32px; border-radius: ${c.radius.md}; background: ${c.bgBrandSolid}; color: #fff; display: flex; align-items: center; justify-content: center; }
43
+ /* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
44
+ the accent (#0298d9) only clears 3.23:1 with white; the darker
45
+ sibling clears 4.5:1+, same fix already applied to Button/Badge/
46
+ Avatar (#897). */
47
+ & .crisp-authshell-logo-badge { flex-shrink: 0; box-sizing: border-box; width: 32px; height: 32px; border-radius: ${c.radius.md}; background: ${c.bgBrandContrast}; color: #fff; display: flex; align-items: center; justify-content: center; }
44
48
  & .crisp-authshell-logo-text { display: flex; flex-direction: column; line-height: 1.2; }
45
49
  & .crisp-authshell-logo-title { font-size: ${c.text.xl}; font-weight: ${c.weight.bold}; letter-spacing: -0.2px; color: ${c.fgPrimary}; }
46
50
  & .crisp-authshell-logo-subtitle { font-size: ${c.text.sm}; color: ${c.fgMuted}; }
@@ -29,7 +29,10 @@ const config = defineRecipe({
29
29
  & .crisp-command-list > .crisp-command-group-label:first-child { padding-top: ${c.space["1"]}; }
30
30
  & .crisp-command-footer { display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; height: 44px; padding: 0 ${c.space["2.5"]}; border-top: 1px solid ${c.borderSubtle}; }
31
31
  & .crisp-command-footer-nav { display: flex; align-items: center; gap: ${c.space["1.5"]}; font-size: ${c.text.sm}; color: ${c.fgMuted}; }
32
- & .crisp-command-footer-action { display: flex; align-items: center; gap: ${c.space["2"]}; height: 28px; max-width: 60%; padding: 0 ${c.space["1"]} 0 ${c.space["2.5"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgBrandSolid}; color: ${c.fgOnBrand}; font-family: inherit; font-size: ${c.text.md}; font-weight: ${c.weight.medium}; cursor: pointer; }
32
+ /* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
33
+ the accent (#0298d9) only clears 3.23:1 with white; the darker sibling
34
+ clears 4.5:1+, same fix already applied to Button/Badge/Avatar (#897). */
35
+ & .crisp-command-footer-action { display: flex; align-items: center; gap: ${c.space["2"]}; height: 28px; max-width: 60%; padding: 0 ${c.space["1"]} 0 ${c.space["2.5"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgBrandContrast}; color: ${c.fgOnBrand}; font-family: inherit; font-size: ${c.text.md}; font-weight: ${c.weight.medium}; cursor: pointer; }
33
36
  & .crisp-command-footer-action:disabled { opacity: 0.5; cursor: default; }
34
37
  & .crisp-command-footer-action-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
35
38
  & .crisp-command-footer-key { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 ${c.space["1"]}; border-radius: ${c.radius.sm}; background: rgba(255,255,255,0.22); font-size: ${c.text.xs}; }
@@ -277,8 +277,13 @@ function CommentRow({ comment, replyLabel, reactLabel, resolveLabel, moreLabel,
277
277
  * nothing on its own beyond that data. See `CommentCountBadge` for the
278
278
  * separate row affordance (P-051) — deliberately NOT part of this
279
279
  * component, since it lives in a table cell this component doesn't own.
280
+ *
281
+ * `variant="drawer"` (default) is today's measured 320px + left-border
282
+ * look; `variant="pane"` (crisp#960) fills its container's width instead,
283
+ * for a consumer embedding this in a full-width tab pane rather than a
284
+ * true slide-over/drawer.
280
285
  */
281
- export function CommentsPanel({ threads, currentUserId, subject, subjectIcon, showResolved = false, onShowResolvedChange, onlyMine = false, onOnlyMineChange, activeThreadId: activeThreadIdProp, onActiveThreadChange, onPost, onReact, onResolve, onDelete, onCopyLink, emojiOptions = DEFAULT_EMOJI, mentionTrigger = "@", renderMentionPicker, onClose, closeLabel: closeLabelProp, backLabel: backLabelProp, title: titleProp, threadTitle: threadTitleProp, breadcrumbPrefix: breadcrumbPrefixProp, composerPlaceholder: composerPlaceholderProp, replyPlaceholder: replyPlaceholderProp, postLabel: postLabelProp, replyPostLabel: replyPostLabelProp, emptyLabel, filterLabel: filterLabelProp, showResolvedLabel: showResolvedLabelProp, onlyMineLabel: onlyMineLabelProp, replyActionLabel: replyActionLabelProp, reactActionLabel: reactActionLabelProp, resolveActionLabel: resolveActionLabelProp, resolvedByYouLabel: resolvedByYouLabelProp, resolvedByLabel, moreActionsLabel: moreActionsLabelProp, copyLinkLabel: copyLinkLabelProp, deleteLabel: deleteLabelProp, addReactionLabel: addReactionLabelProp, reactionSearchLabel: reactionSearchLabelProp, deleteConfirmTitle: deleteConfirmTitleProp, deleteConfirmBody: deleteConfirmBodyProp, deleteConfirmActionLabel: deleteConfirmActionLabelProp, cancelLabel: cancelLabelProp, composerMentionToolLabel: composerMentionToolLabelProp, className, ...rest }) {
286
+ export function CommentsPanel({ threads, currentUserId, subject, subjectIcon, showResolved = false, onShowResolvedChange, onlyMine = false, onOnlyMineChange, activeThreadId: activeThreadIdProp, onActiveThreadChange, onPost, onReact, onResolve, onDelete, onCopyLink, emojiOptions = DEFAULT_EMOJI, mentionTrigger = "@", renderMentionPicker, onClose, closeLabel: closeLabelProp, backLabel: backLabelProp, title: titleProp, threadTitle: threadTitleProp, breadcrumbPrefix: breadcrumbPrefixProp, composerPlaceholder: composerPlaceholderProp, replyPlaceholder: replyPlaceholderProp, postLabel: postLabelProp, replyPostLabel: replyPostLabelProp, emptyLabel, filterLabel: filterLabelProp, showResolvedLabel: showResolvedLabelProp, onlyMineLabel: onlyMineLabelProp, replyActionLabel: replyActionLabelProp, reactActionLabel: reactActionLabelProp, resolveActionLabel: resolveActionLabelProp, resolvedByYouLabel: resolvedByYouLabelProp, resolvedByLabel, moreActionsLabel: moreActionsLabelProp, copyLinkLabel: copyLinkLabelProp, deleteLabel: deleteLabelProp, addReactionLabel: addReactionLabelProp, reactionSearchLabel: reactionSearchLabelProp, deleteConfirmTitle: deleteConfirmTitleProp, deleteConfirmBody: deleteConfirmBodyProp, deleteConfirmActionLabel: deleteConfirmActionLabelProp, cancelLabel: cancelLabelProp, composerMentionToolLabel: composerMentionToolLabelProp, variant, className, ...rest }) {
282
287
  const closeLabel = useCrispMessage(closeLabelProp, "Close");
283
288
  const backLabel = useCrispMessage(backLabelProp, "Back");
284
289
  const title = useCrispMessage(titleProp, "All comments");
@@ -331,7 +336,7 @@ export function CommentsPanel({ threads, currentUserId, subject, subjectIcon, sh
331
336
  const activeThread = activeThreadId
332
337
  ? threads.find((t) => t.id === activeThreadId)
333
338
  : null;
334
- return (_jsxs("div", { className: commentsRecipe({ className }), role: "dialog", "aria-label": title, ...rest, children: [_jsxs("div", { className: "crisp-comments-head", children: [activeThread ? (_jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": backLabel, onClick: () => setActiveThreadId(null), children: _jsx(ChevronLeft, { "data-rtl-flip": "", size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) })) : null, _jsxs("div", { className: "crisp-comments-title-wrap", children: [_jsx("span", { className: "crisp-comments-title", children: activeThread ? threadTitle : title }), activeThread && (_jsxs("span", { className: "crisp-comments-breadcrumb", children: [breadcrumbPrefix, " ", subjectIcon, " ", subject] }))] }), !activeThread && (_jsxs(Menu, { align: "end", trigger: _jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": filterLabel, children: _jsx(ListFilter, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), children: [_jsx(MenuCheckboxItem, { checked: showResolved, onCheckedChange: onShowResolvedChange, children: showResolvedLabel }), _jsx(MenuCheckboxItem, { checked: onlyMine, onCheckedChange: onOnlyMineChange, children: onlyMineLabel })] })), onClose && (_jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": closeLabel, onClick: onClose, children: _jsx(X, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }))] }), _jsx("div", { className: "crisp-comments-body crisp-comments-body--pad-top", children: activeThread ? (_jsxs("div", { children: [_jsx(CommentRow, { comment: activeThread.root, currentUserId: currentUserId, replyLabel: replyActionLabel, reactLabel: reactActionLabel, resolveLabel: resolveActionLabel, moreLabel: moreActionsLabel, copyLinkLabel: copyLinkLabel, deleteLabel: deleteLabel, addReactionLabel: addReactionLabel, reactionSearchLabel: reactionSearchLabel, emojiOptions: emojiOptions, resolvable: !!onResolve, resolved: activeThread.resolved, resolvedLabel: activeThread.resolved
339
+ return (_jsxs("div", { className: commentsRecipe({ variant, className }), role: "dialog", "aria-label": title, ...rest, children: [_jsxs("div", { className: "crisp-comments-head", children: [activeThread ? (_jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": backLabel, onClick: () => setActiveThreadId(null), children: _jsx(ChevronLeft, { "data-rtl-flip": "", size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) })) : null, _jsxs("div", { className: "crisp-comments-title-wrap", children: [_jsx("span", { className: "crisp-comments-title", children: activeThread ? threadTitle : title }), activeThread && (_jsxs("span", { className: "crisp-comments-breadcrumb", children: [breadcrumbPrefix, " ", subjectIcon, " ", subject] }))] }), !activeThread && (_jsxs(Menu, { align: "end", trigger: _jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": filterLabel, children: _jsx(ListFilter, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), children: [_jsx(MenuCheckboxItem, { checked: showResolved, onCheckedChange: onShowResolvedChange, children: showResolvedLabel }), _jsx(MenuCheckboxItem, { checked: onlyMine, onCheckedChange: onOnlyMineChange, children: onlyMineLabel })] })), onClose && (_jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": closeLabel, onClick: onClose, children: _jsx(X, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }))] }), _jsx("div", { className: "crisp-comments-body crisp-comments-body--pad-top", children: activeThread ? (_jsxs("div", { children: [_jsx(CommentRow, { comment: activeThread.root, currentUserId: currentUserId, replyLabel: replyActionLabel, reactLabel: reactActionLabel, resolveLabel: resolveActionLabel, moreLabel: moreActionsLabel, copyLinkLabel: copyLinkLabel, deleteLabel: deleteLabel, addReactionLabel: addReactionLabel, reactionSearchLabel: reactionSearchLabel, emojiOptions: emojiOptions, resolvable: !!onResolve, resolved: activeThread.resolved, resolvedLabel: activeThread.resolved
335
340
  ? activeThread.resolvedBy?.id === currentUserId ||
336
341
  !activeThread.resolvedBy
337
342
  ? resolvedByYouLabel
@@ -15,8 +15,8 @@ import { contract as c, elevation as e } from "../../tokens/index.js";
15
15
  // width and still gets an odd number).
16
16
  const config = defineRecipe({
17
17
  name: "comments",
18
- base: `display: flex; flex-direction: column; box-sizing: border-box; width: 320px; height: 100%;
19
- background: ${c.bgSurface}; border-inline-start: 1px solid ${c.borderSubtle};
18
+ base: `display: flex; flex-direction: column; box-sizing: border-box; height: 100%;
19
+ background: ${c.bgSurface};
20
20
  font-family: ${c.font.sans};
21
21
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
22
22
  & * { box-sizing: border-box; }
@@ -96,9 +96,13 @@ const config = defineRecipe({
96
96
  box-shadow, so this reuses that literal rather than re-deriving it.
97
97
  32px/radius.lg/6px-10px padding has no matching Button \`size\` yet
98
98
  (24/28/40 are the three that exist), so this stays hand-rolled instead
99
- of stretching one of those. */
100
- & .crisp-comments-post { display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 6px ${c.space["2.5"]}; border: none; border-radius: ${c.radius.lg}; background: ${c.bgBrandSolid}; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08); font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.semibold}; color: ${c.fgOnBrand}; cursor: pointer; opacity: 1; transition: opacity .12s, background-color .12s; }
101
- & .crisp-comments-post:hover:not(:disabled) { background: ${c.bgBrandSolidPressed}; }
99
+ of stretching one of those. your-moon/crisp#953:
100
+ bgBrandContrast, not bgBrandSolid -- the accent (#0298d9) only clears
101
+ 3.23:1 with white; same fix already applied to Button/Badge/Avatar
102
+ (#897). Hover is a brightness filter on the new fill, matching that
103
+ PR's own Button primary treatment, not bgBrandSolidPressed. */
104
+ & .crisp-comments-post { display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 6px ${c.space["2.5"]}; border: none; border-radius: ${c.radius.lg}; background: ${c.bgBrandContrast}; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08); font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.semibold}; color: ${c.fgOnBrand}; cursor: pointer; opacity: 1; transition: opacity .12s, filter .12s; }
105
+ & .crisp-comments-post:hover:not(:disabled) { filter: brightness(0.96); }
102
106
  & .crisp-comments-post:disabled { opacity: .4; cursor: default; }
103
107
 
104
108
  /* Filter menu -- measured: 220px, radius 12 (radius.xl), 4px padding,
@@ -110,8 +114,22 @@ const config = defineRecipe({
110
114
  rather than re-derived since it's the same atomic-chip affordance. */
111
115
  & .crisp-comments-mention { display: inline; padding: 0 2px; border-radius: 4px; background: color-mix(in srgb, ${c.bgBrandSolid} 12%, transparent); color: ${c.bgBrandSolid}; font-weight: ${c.weight.medium}; user-select: none; }
112
116
  ${rtlFlipCss}`,
113
- variants: {},
114
- defaultVariants: {},
117
+ variants: {
118
+ // your-moon/crisp#960: the 320px width + left-border hairline is a
119
+ // drawer-consumer look (RecordPage's own comments drawer, #864), not
120
+ // an intrinsic property of the panel itself. A consumer embedding this
121
+ // in a full-width tab pane (internal/grc's own record-page Comments
122
+ // tab, sibling to Related/Activity/Notes/Tasks/Lists, all of which
123
+ // fill the pane) needs `width: 100%` and no border -- a tab pane
124
+ // doesn't need a seam separating it from itself. `drawer` (default)
125
+ // is today's exact, already-measured look; nothing changes for an
126
+ // existing consumer that doesn't pass `variant`.
127
+ variant: {
128
+ drawer: `width: 320px; border-inline-start: 1px solid ${c.borderSubtle};`,
129
+ pane: `width: 100%;`,
130
+ },
131
+ },
132
+ defaultVariants: { variant: "drawer" },
115
133
  compoundVariants: [],
116
134
  // Two pieces below intentionally live OUTSIDE the nested \`base\` above,
117
135
  // the same reason Menu's own \`.crisp-menu-content\` does (menu.recipe.ts's
@@ -0,0 +1 @@
1
+ export { InviteMembersDialog, MembersSettings, } from "./memberssettings.js";