@crisp-ui-kit/crisp 0.57.1 → 0.57.3

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 (277) hide show
  1. package/dist/authshell.css +9 -4
  2. package/dist/authshell.layered.css +9 -4
  3. package/dist/badge.css +3 -3
  4. package/dist/badge.layered.css +3 -3
  5. package/dist/board.css +28 -8
  6. package/dist/board.layered.css +28 -8
  7. package/dist/bottomsheet.css +26 -0
  8. package/dist/bottomsheet.d.ts +7 -0
  9. package/dist/bottomsheet.layered.css +28 -0
  10. package/dist/button.css +10 -6
  11. package/dist/button.d.ts +2 -2
  12. package/dist/button.layered.css +10 -6
  13. package/dist/checkbox.css +7 -3
  14. package/dist/checkbox.layered.css +7 -3
  15. package/dist/chip.css +16 -1
  16. package/dist/chip.d.ts +3 -1
  17. package/dist/chip.layered.css +16 -1
  18. package/dist/chipgroup.css +5 -0
  19. package/dist/chipgroup.d.ts +7 -0
  20. package/dist/chipgroup.layered.css +7 -0
  21. package/dist/cjs/components/badge/badge.recipe.js +22 -8
  22. package/dist/cjs/components/bottomsheet/bottomsheet.js +90 -0
  23. package/dist/cjs/components/bottomsheet/bottomsheet.recipe.js +77 -0
  24. package/dist/cjs/components/bottomsheet/index.js +9 -0
  25. package/dist/cjs/components/breadcrumb/breadcrumb.js +1 -1
  26. package/dist/cjs/components/button/button.recipe.js +67 -15
  27. package/dist/cjs/components/checkbox/checkbox.recipe.js +61 -9
  28. package/dist/cjs/components/chip/chip.js +14 -2
  29. package/dist/cjs/components/chip/chip.recipe.js +72 -2
  30. package/dist/cjs/components/chipgroup/chipgroup.js +51 -0
  31. package/dist/cjs/components/chipgroup/chipgroup.recipe.js +36 -0
  32. package/dist/cjs/components/chipgroup/index.js +7 -0
  33. package/dist/cjs/components/datatable/datatable.js +5 -1
  34. package/dist/cjs/components/datatable/datatable.recipe.js +21 -6
  35. package/dist/cjs/components/datepickerfield/datepickerfield.js +2 -2
  36. package/dist/cjs/components/datepickerfield/datepickerfield.recipe.js +16 -3
  37. package/dist/cjs/components/dialog/dialog.recipe.js +21 -10
  38. package/dist/cjs/components/emptystate/emptystate.recipe.js +7 -2
  39. package/dist/cjs/components/fileupload/fileupload.js +21 -4
  40. package/dist/cjs/components/input/index.js +2 -1
  41. package/dist/cjs/components/input/input.js +31 -1
  42. package/dist/cjs/components/input/input.recipe.js +16 -9
  43. package/dist/cjs/components/menu/menu.js +18 -2
  44. package/dist/cjs/components/menu/menu.recipe.js +21 -7
  45. package/dist/cjs/components/menusheet/index.js +9 -0
  46. package/dist/cjs/components/menusheet/menusheet.js +133 -0
  47. package/dist/cjs/components/menusheet/menusheet.recipe.js +103 -0
  48. package/dist/cjs/components/multiselect/multiselect.js +1 -1
  49. package/dist/cjs/components/multiselect/multiselect.recipe.js +33 -9
  50. package/dist/cjs/components/select/select.js +1 -1
  51. package/dist/cjs/components/select/select.recipe.js +38 -12
  52. package/dist/cjs/components/snackbar/index.js +8 -0
  53. package/dist/cjs/components/snackbar/snackbar.js +121 -0
  54. package/dist/cjs/components/snackbar/snackbar.recipe.js +71 -0
  55. package/dist/cjs/components/table/table.recipe.js +7 -1
  56. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +7 -2
  57. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +37 -12
  58. package/dist/cjs/components/tabs/tabs.js +10 -4
  59. package/dist/cjs/components/tabs/tabs.recipe.js +23 -6
  60. package/dist/cjs/components/textarea/textarea.recipe.js +11 -8
  61. package/dist/cjs/core/floating-portal.js +7 -4
  62. package/dist/cjs/index.js +7 -0
  63. package/dist/cjs/product/authshell/authshell.recipe.js +5 -1
  64. package/dist/cjs/product/command/command.recipe.js +4 -1
  65. package/dist/cjs/product/comments/comments.js +7 -2
  66. package/dist/cjs/product/comments/comments.recipe.js +25 -7
  67. package/dist/cjs/product/inbox/inbox.js +11 -3
  68. package/dist/cjs/product/memberssettings/index.js +6 -0
  69. package/dist/cjs/product/memberssettings/memberssettings.js +99 -0
  70. package/dist/cjs/product/memberssettings/memberssettings.recipe.js +83 -0
  71. package/dist/cjs/product/notificationssettings/index.js +5 -0
  72. package/dist/cjs/product/notificationssettings/notificationssettings.js +19 -0
  73. package/dist/cjs/product/notificationssettings/notificationssettings.recipe.js +44 -0
  74. package/dist/cjs/product/recordpage/index.js +2 -1
  75. package/dist/cjs/product/recordpage/recordpage.js +106 -9
  76. package/dist/cjs/product/recordpage/recordpage.recipe.js +68 -6
  77. package/dist/cjs/product/workspacegeneralsettings/index.js +5 -0
  78. package/dist/cjs/product/workspacegeneralsettings/workspacegeneralsettings.js +21 -0
  79. package/dist/cjs/product/workspacegeneralsettings/workspacegeneralsettings.recipe.js +51 -0
  80. package/dist/cjs/tokens/contract.js +1 -0
  81. package/dist/cjs/tokens/elevation.js +8 -5
  82. package/dist/cjs/tokens/palette.js +28 -8
  83. package/dist/cjs/tokens/scale.js +44 -2
  84. package/dist/cjs/tokens/semantic.js +2 -0
  85. package/dist/columncalc.css +15 -7
  86. package/dist/columncalc.layered.css +15 -7
  87. package/dist/command.css +23 -19
  88. package/dist/command.layered.css +23 -19
  89. package/dist/comments.css +28 -19
  90. package/dist/comments.d.ts +2 -2
  91. package/dist/comments.layered.css +28 -19
  92. package/dist/datatable.css +171 -71
  93. package/dist/datatable.layered.css +171 -71
  94. package/dist/datepicker.css +13 -41
  95. package/dist/datepicker.layered.css +13 -41
  96. package/dist/datepickerfield.css +12 -3
  97. package/dist/datepickerfield.d.ts +2 -2
  98. package/dist/datepickerfield.layered.css +12 -3
  99. package/dist/dialog.css +12 -7
  100. package/dist/dialog.layered.css +12 -7
  101. package/dist/emptystate.css +5 -3
  102. package/dist/emptystate.layered.css +5 -3
  103. package/dist/esm/components/badge/badge.recipe.js +22 -8
  104. package/dist/esm/components/bottomsheet/bottomsheet.js +52 -0
  105. package/dist/esm/components/bottomsheet/bottomsheet.recipe.js +74 -0
  106. package/dist/esm/components/bottomsheet/index.js +2 -0
  107. package/dist/esm/components/breadcrumb/breadcrumb.js +1 -1
  108. package/dist/esm/components/button/button.recipe.js +68 -16
  109. package/dist/esm/components/checkbox/checkbox.recipe.js +61 -9
  110. package/dist/esm/components/chip/chip.js +14 -2
  111. package/dist/esm/components/chip/chip.recipe.js +72 -2
  112. package/dist/esm/components/chipgroup/chipgroup.js +15 -0
  113. package/dist/esm/components/chipgroup/chipgroup.recipe.js +33 -0
  114. package/dist/esm/components/chipgroup/index.js +2 -0
  115. package/dist/esm/components/datatable/datatable.js +5 -1
  116. package/dist/esm/components/datatable/datatable.recipe.js +21 -6
  117. package/dist/esm/components/datepickerfield/datepickerfield.js +2 -2
  118. package/dist/esm/components/datepickerfield/datepickerfield.recipe.js +16 -3
  119. package/dist/esm/components/dialog/dialog.recipe.js +22 -11
  120. package/dist/esm/components/emptystate/emptystate.recipe.js +7 -2
  121. package/dist/esm/components/fileupload/fileupload.js +20 -5
  122. package/dist/esm/components/input/index.js +1 -1
  123. package/dist/esm/components/input/input.js +30 -0
  124. package/dist/esm/components/input/input.recipe.js +17 -10
  125. package/dist/esm/components/menu/menu.js +18 -2
  126. package/dist/esm/components/menu/menu.recipe.js +21 -7
  127. package/dist/esm/components/menusheet/index.js +2 -0
  128. package/dist/esm/components/menusheet/menusheet.js +95 -0
  129. package/dist/esm/components/menusheet/menusheet.recipe.js +100 -0
  130. package/dist/esm/components/multiselect/multiselect.js +1 -1
  131. package/dist/esm/components/multiselect/multiselect.recipe.js +33 -9
  132. package/dist/esm/components/select/select.js +1 -1
  133. package/dist/esm/components/select/select.recipe.js +38 -12
  134. package/dist/esm/components/snackbar/index.js +2 -0
  135. package/dist/esm/components/snackbar/snackbar.js +84 -0
  136. package/dist/esm/components/snackbar/snackbar.recipe.js +68 -0
  137. package/dist/esm/components/table/table.recipe.js +7 -1
  138. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +7 -2
  139. package/dist/esm/components/tabletoolbar/tabletoolbar.js +37 -12
  140. package/dist/esm/components/tabs/tabs.js +10 -4
  141. package/dist/esm/components/tabs/tabs.recipe.js +23 -6
  142. package/dist/esm/components/textarea/textarea.recipe.js +12 -9
  143. package/dist/esm/core/floating-portal.js +7 -4
  144. package/dist/esm/index.js +7 -0
  145. package/dist/esm/product/authshell/authshell.recipe.js +5 -1
  146. package/dist/esm/product/command/command.recipe.js +4 -1
  147. package/dist/esm/product/comments/comments.js +7 -2
  148. package/dist/esm/product/comments/comments.recipe.js +25 -7
  149. package/dist/esm/product/inbox/inbox.js +11 -3
  150. package/dist/esm/product/memberssettings/index.js +1 -0
  151. package/dist/esm/product/memberssettings/memberssettings.js +62 -0
  152. package/dist/esm/product/memberssettings/memberssettings.recipe.js +80 -0
  153. package/dist/esm/product/notificationssettings/index.js +1 -0
  154. package/dist/esm/product/notificationssettings/notificationssettings.js +16 -0
  155. package/dist/esm/product/notificationssettings/notificationssettings.recipe.js +41 -0
  156. package/dist/esm/product/recordpage/index.js +1 -1
  157. package/dist/esm/product/recordpage/recordpage.js +106 -10
  158. package/dist/esm/product/recordpage/recordpage.recipe.js +68 -6
  159. package/dist/esm/product/workspacegeneralsettings/index.js +1 -0
  160. package/dist/esm/product/workspacegeneralsettings/workspacegeneralsettings.js +18 -0
  161. package/dist/esm/product/workspacegeneralsettings/workspacegeneralsettings.recipe.js +48 -0
  162. package/dist/esm/tokens/contract.js +2 -1
  163. package/dist/esm/tokens/elevation.js +8 -5
  164. package/dist/esm/tokens/palette.js +28 -8
  165. package/dist/esm/tokens/scale.js +43 -1
  166. package/dist/esm/tokens/semantic.js +2 -0
  167. package/dist/fileupload.css +3 -1
  168. package/dist/fileupload.layered.css +3 -1
  169. package/dist/input.css +6 -4
  170. package/dist/input.layered.css +6 -4
  171. package/dist/listpage.css +3 -1
  172. package/dist/listpage.layered.css +3 -1
  173. package/dist/memberssettings.css +67 -0
  174. package/dist/memberssettings.d.ts +7 -0
  175. package/dist/memberssettings.layered.css +69 -0
  176. package/dist/menu.css +2 -2
  177. package/dist/menu.layered.css +2 -2
  178. package/dist/menusheet.css +36 -0
  179. package/dist/menusheet.d.ts +7 -0
  180. package/dist/menusheet.layered.css +38 -0
  181. package/dist/multiselect.css +13 -6
  182. package/dist/multiselect.layered.css +13 -6
  183. package/dist/native/tokens.d.ts +2 -1
  184. package/dist/native/tokens.js +20 -3
  185. package/dist/notificationssettings.css +22 -0
  186. package/dist/notificationssettings.d.ts +7 -0
  187. package/dist/notificationssettings.layered.css +24 -0
  188. package/dist/objectsettings.css +4 -4
  189. package/dist/objectsettings.layered.css +4 -4
  190. package/dist/recordpage.css +90 -23
  191. package/dist/recordpage.layered.css +90 -23
  192. package/dist/recordpanel.css +35 -16
  193. package/dist/recordpanel.layered.css +35 -16
  194. package/dist/resizablepanel.css +8 -5
  195. package/dist/resizablepanel.layered.css +8 -5
  196. package/dist/richtext.css +24 -8
  197. package/dist/richtext.layered.css +24 -8
  198. package/dist/savedviews.css +9 -2
  199. package/dist/savedviews.layered.css +9 -2
  200. package/dist/select.css +25 -5
  201. package/dist/select.layered.css +25 -5
  202. package/dist/settingsshell.css +2 -3
  203. package/dist/settingsshell.layered.css +2 -3
  204. package/dist/slider.css +6 -27
  205. package/dist/slider.layered.css +6 -27
  206. package/dist/snackbar.css +24 -0
  207. package/dist/snackbar.d.ts +7 -0
  208. package/dist/snackbar.layered.css +26 -0
  209. package/dist/statcard.css +3 -1
  210. package/dist/statcard.layered.css +3 -1
  211. package/dist/styles.css +911 -354
  212. package/dist/styles.layered.css +911 -354
  213. package/dist/table.css +35 -12
  214. package/dist/table.layered.css +35 -12
  215. package/dist/tabletoolbar.css +35 -24
  216. package/dist/tabletoolbar.layered.css +35 -24
  217. package/dist/tabs.css +18 -4
  218. package/dist/tabs.layered.css +18 -4
  219. package/dist/textarea.css +2 -6
  220. package/dist/textarea.layered.css +2 -6
  221. package/dist/themepicker.css +2 -1
  222. package/dist/themepicker.layered.css +2 -1
  223. package/dist/tokens.css +13 -2
  224. package/dist/tokens.json +20 -3
  225. package/dist/types/components/bottomsheet/bottomsheet.d.ts +35 -0
  226. package/dist/types/components/bottomsheet/bottomsheet.recipe.d.ts +3 -0
  227. package/dist/types/components/bottomsheet/index.d.ts +2 -0
  228. package/dist/types/components/breadcrumb/breadcrumb.d.ts +9 -1
  229. package/dist/types/components/button/button.d.ts +2 -0
  230. package/dist/types/components/button/button.recipe.d.ts +24 -6
  231. package/dist/types/components/checkbox/checkbox.recipe.d.ts +28 -5
  232. package/dist/types/components/chip/chip.d.ts +22 -0
  233. package/dist/types/components/chip/chip.recipe.d.ts +49 -1
  234. package/dist/types/components/chipgroup/chipgroup.d.ts +14 -0
  235. package/dist/types/components/chipgroup/chipgroup.recipe.d.ts +5 -0
  236. package/dist/types/components/chipgroup/index.d.ts +2 -0
  237. package/dist/types/components/datatable/datatable.d.ts +19 -0
  238. package/dist/types/components/datepickerfield/datepickerfield.d.ts +4 -1
  239. package/dist/types/components/datepickerfield/datepickerfield.recipe.d.ts +18 -3
  240. package/dist/types/components/fileupload/fileupload.d.ts +26 -0
  241. package/dist/types/components/input/index.d.ts +1 -1
  242. package/dist/types/components/input/input.d.ts +72 -0
  243. package/dist/types/components/menusheet/index.d.ts +2 -0
  244. package/dist/types/components/menusheet/menusheet.d.ts +42 -0
  245. package/dist/types/components/menusheet/menusheet.recipe.d.ts +3 -0
  246. package/dist/types/components/snackbar/index.d.ts +2 -0
  247. package/dist/types/components/snackbar/snackbar.d.ts +36 -0
  248. package/dist/types/components/snackbar/snackbar.recipe.d.ts +5 -0
  249. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +28 -1
  250. package/dist/types/components/tabs/tabs.d.ts +11 -1
  251. package/dist/types/core/floating-portal.d.ts +6 -5
  252. package/dist/types/index.d.ts +7 -0
  253. package/dist/types/product/comments/comments.d.ts +9 -3
  254. package/dist/types/product/comments/comments.recipe.d.ts +12 -2
  255. package/dist/types/product/inbox/inbox.d.ts +13 -1
  256. package/dist/types/product/memberssettings/index.d.ts +1 -0
  257. package/dist/types/product/memberssettings/memberssettings.d.ts +85 -0
  258. package/dist/types/product/memberssettings/memberssettings.recipe.d.ts +3 -0
  259. package/dist/types/product/notificationssettings/index.d.ts +1 -0
  260. package/dist/types/product/notificationssettings/notificationssettings.d.ts +37 -0
  261. package/dist/types/product/notificationssettings/notificationssettings.recipe.d.ts +3 -0
  262. package/dist/types/product/recordpage/index.d.ts +1 -1
  263. package/dist/types/product/recordpage/recordpage.d.ts +99 -2
  264. package/dist/types/product/workspacegeneralsettings/index.d.ts +1 -0
  265. package/dist/types/product/workspacegeneralsettings/workspacegeneralsettings.d.ts +48 -0
  266. package/dist/types/product/workspacegeneralsettings/workspacegeneralsettings.recipe.d.ts +3 -0
  267. package/dist/types/tokens/contract.d.ts +4 -1
  268. package/dist/types/tokens/elevation.d.ts +8 -5
  269. package/dist/types/tokens/palette.d.ts +9 -1
  270. package/dist/types/tokens/scale.d.ts +11 -1
  271. package/dist/types/tokens/semantic.d.ts +2 -0
  272. package/dist/wheelpicker.css +4 -2
  273. package/dist/wheelpicker.layered.css +4 -2
  274. package/dist/workspacegeneralsettings.css +30 -0
  275. package/dist/workspacegeneralsettings.d.ts +7 -0
  276. package/dist/workspacegeneralsettings.layered.css +32 -0
  277. package/package.json +36 -1
@@ -179,6 +179,11 @@ const toolbarChromeBody = (p) => ` & .${namespace_1.CLASS_PREFIX}${p}-menu-he
179
179
  & .${namespace_1.CLASS_PREFIX}${p}-fchip-x:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 10%, transparent); color: ${tokens_1.contract.fgPrimary}; }
180
180
  & .${namespace_1.CLASS_PREFIX}${p}-spacer { flex: 1; }
181
181
  & .${namespace_1.CLASS_PREFIX}${p}-count { font-size: ${tokens_1.contract.text.md}; color: ${tokens_1.contract.fgMuted}; }
182
- & .${namespace_1.CLASS_PREFIX}${p}-new { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; height: ${tokens_1.contract.size.row}; padding: 0 ${tokens_1.contract.space["2.5"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgBrandSolid}; color: ${tokens_1.contract.fgOnBrand}; font-family: inherit; font-size: ${tokens_1.contract.text.md}; font-weight: ${tokens_1.contract.weight.medium}; cursor: pointer; transition: background-color .12s; }
183
- & .${namespace_1.CLASS_PREFIX}${p}-new:hover { background: ${tokens_1.contract.bgBrandSolidPressed}; }`;
182
+ /* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
183
+ the accent (#0298d9) only clears 3.23:1 with white; the darker sibling
184
+ clears 4.5:1+, same fix already applied to Button/Badge/Avatar (#897).
185
+ Hover is a brightness filter on the new fill, not bgBrandSolidPressed
186
+ (that token stays tuned for the lighter accent's own consumers). */
187
+ & .${namespace_1.CLASS_PREFIX}${p}-new { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; height: ${tokens_1.contract.size.row}; padding: 0 ${tokens_1.contract.space["2.5"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgBrandContrast}; color: ${tokens_1.contract.fgOnBrand}; font-family: inherit; font-size: ${tokens_1.contract.text.md}; font-weight: ${tokens_1.contract.weight.medium}; cursor: pointer; transition: filter .12s; }
188
+ & .${namespace_1.CLASS_PREFIX}${p}-new:hover { filter: brightness(0.96); }`;
184
189
  exports.toolbarChromeBody = toolbarChromeBody;
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.ToolbarTool = void 0;
36
+ exports.ToolbarTool = exports.toolbarOperatorLabels = void 0;
37
37
  exports.applyFilters = applyFilters;
38
38
  exports.applySorts = applySorts;
39
39
  exports.TableToolbar = TableToolbar;
@@ -83,7 +83,7 @@ function ViewBarSlot({ target, children, }) {
83
83
  // file, not per call site, and three independently attributed tags for
84
84
  // the same name over-counts against that (it expects one per name).
85
85
  const RtlChevronRight = ({ size, className, }) => ((0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { "data-rtl-flip": "", size: size, strokeWidth: 1.75, className: className }));
86
- const OP_LABELS = {
86
+ exports.toolbarOperatorLabels = {
87
87
  contains: "contains",
88
88
  doesnotcontain: "does not contain",
89
89
  startswith: "starts with",
@@ -104,6 +104,33 @@ const OP_LABELS = {
104
104
  before: "before",
105
105
  after: "after",
106
106
  };
107
+ /**
108
+ * crisp/#962: resolves each operator word through four layers, explicit
109
+ * prop winning, same "prop beats provider beats built-in" order as every
110
+ * other label in this file — `operatorLabels` prop, then a
111
+ * `CrispMessagesProvider`'s `componentDefaults.tableToolbar` (the new,
112
+ * discoverable, typed override), then the OLD whole-`CrispMessages`-
113
+ * dictionary-keyed-by-English-string lookup this file already had (kept
114
+ * so a consumer who already added entries there keeps working, unchanged),
115
+ * then the hardcoded English word. Not `useCrispMessage` in a loop over 13
116
+ * keys — that breaks the rules of hooks the same reason `useCrispMessages`
117
+ * itself exists (see that hook's own doc comment).
118
+ */
119
+ function useToolbarOperatorLabels(operatorLabels, crispMessages) {
120
+ const providerLabels = (0, core_1.useCrispComponentDefaults)("tableToolbar");
121
+ return React.useMemo(() => {
122
+ const resolved = {};
123
+ for (const op of Object.keys(exports.toolbarOperatorLabels)) {
124
+ const builtIn = exports.toolbarOperatorLabels[op];
125
+ resolved[op] =
126
+ operatorLabels?.[op] ??
127
+ providerLabels?.[op] ??
128
+ crispMessages[builtIn] ??
129
+ builtIn;
130
+ }
131
+ return resolved;
132
+ }, [operatorLabels, providerLabels, crispMessages]);
133
+ }
107
134
  // #619: order and label set match Attio's own text-filter operator picker
108
135
  // verbatim (measured live, per RecordsTable's own #578 port of the same
109
136
  // set: contains · does not contain · starts with · ends with · is · is not ·
@@ -368,7 +395,7 @@ function applySorts(rows, sorts, get = (row, key) => row[key]) {
368
395
  * this (Sort: Due date/Assignee/Creation date; Filter: Assignee/Record/
369
396
  * Created by), which one shared list can't reproduce.
370
397
  */
371
- 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 }) {
398
+ 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 }) {
372
399
  // crisp/#471: an explicit prop always wins; missing, it reads the nearest
373
400
  // CrispMessagesProvider's translation of the English default below, or
374
401
  // that default itself with no provider — unchanged from before this
@@ -401,12 +428,12 @@ function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: fi
401
428
  const exportExcelLabel = (0, core_1.useCrispMessage)(exportExcelLabelProp, "Export view as Excel");
402
429
  const learnImportExportLabel = (0, core_1.useCrispMessage)(learnImportExportLabelProp, "Learn about imports and exports");
403
430
  const backLabel = (0, core_1.useCrispMessage)(backLabelProp, "Back");
404
- // OP_LABELS' own words ("contains", "is", "is any of", …) have no prop of
405
- // their own — a whole lookup table, not one string — so they translate
406
- // straight from the provider's dictionary at the two spots they render
407
- // (#628, #673 below), same fallback order as every resolved constant
408
- // above.
409
431
  const crispMessages = (0, core_1.useCrispMessages)();
432
+ // crisp/#962: `operatorLabels` prop + `componentDefaults.tableToolbar`,
433
+ // layered over the old whole-dictionary lookup — see
434
+ // `useToolbarOperatorLabels`'s own doc comment for the full resolution
435
+ // order.
436
+ const opLabels = useToolbarOperatorLabels(operatorLabels, crispMessages);
410
437
  // Each side falls back to the shared `columns` independently — a caller
411
438
  // that only overrides one (Filter's own Record/Created by, say) still
412
439
  // gets `columns` on the other, unchanged from before either prop existed.
@@ -842,9 +869,7 @@ function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: fi
842
869
  return ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-tabletoolbar-toolgroup", children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-tabletoolbar-fcond", "data-complete": complete ? "" : undefined, children: [(0, jsx_runtime_1.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: () => {
843
870
  setFieldPickerPath([]);
844
871
  setOpenMenu(fieldOpen ? null : `ffield:${cond.id}`);
845
- }, children: [col?.icon ? (0, jsx_runtime_1.jsx)(col.icon, { size: 14, strokeWidth: 1.75 }) : null, colPath.length > 1 ? ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabletoolbar-fcond-path", children: colPath.map((c, i) => ((0, jsx_runtime_1.jsxs)(React.Fragment, { children: [i > 0 && ((0, jsx_runtime_1.jsx)(RtlChevronRight, { size: 14, className: "crisp-tabletoolbar-fcond-path-sep" })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-tabletoolbar-fcond-path-seg", children: c.label })] }, c.key))) })) : (col?.label)] }), (0, jsx_runtime_1.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
846
- ? (crispMessages[OP_LABELS[cond.op]] ?? OP_LABELS[cond.op])
847
- : selectConditionLabel }), showValueSeg && ((0, jsx_runtime_1.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"
872
+ }, children: [col?.icon ? (0, jsx_runtime_1.jsx)(col.icon, { size: 14, strokeWidth: 1.75 }) : null, colPath.length > 1 ? ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabletoolbar-fcond-path", children: colPath.map((c, i) => ((0, jsx_runtime_1.jsxs)(React.Fragment, { children: [i > 0 && ((0, jsx_runtime_1.jsx)(RtlChevronRight, { size: 14, className: "crisp-tabletoolbar-fcond-path-sep" })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-tabletoolbar-fcond-path-seg", children: c.label })] }, c.key))) })) : (col?.label)] }), (0, jsx_runtime_1.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 && ((0, jsx_runtime_1.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"
848
873
  ? cond.values?.length
849
874
  ? isAnyOfSummary(cond.values)
850
875
  : filterValuePlaceholder
@@ -852,7 +877,7 @@ function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: fi
852
877
  ? col?.type === "date"
853
878
  ? formatFilterDate(cond.value)
854
879
  : cond.value
855
- : filterValuePlaceholder })), (0, jsx_runtime_1.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: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreVertical, { size: 13, strokeWidth: 2 }) })] }), kebabOpen && ((0, jsx_runtime_1.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: (0, jsx_runtime_1.jsxs)("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item crisp-tabletoolbar-menu-item--danger", onClick: () => removeFilter(cond.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 16, strokeWidth: 1.75 }), deleteFilterLabel] }) })), fieldOpen && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabletoolbar-menu", role: "dialog", "aria-label": filterFieldLabel, children: nestedFieldPicker(filterColumns, (key) => changeFilterField(cond.id, key), filterFieldPlaceholder, filterFieldLabel) })), opOpen && ((0, jsx_runtime_1.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) => ((0, jsx_runtime_1.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 ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "crisp-tabletoolbar-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))) })), valOpen && showValueSeg && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--val", role: "dialog", "aria-label": `${col?.label} filter value`, children: cond.op === "isanyof" && col?.values ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("input", { className: "crisp-tabletoolbar-menu-input", placeholder: filterValuePlaceholder, "aria-label": `${col?.label} filter values`,
880
+ : filterValuePlaceholder })), (0, jsx_runtime_1.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: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreVertical, { size: 13, strokeWidth: 2 }) })] }), kebabOpen && ((0, jsx_runtime_1.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: (0, jsx_runtime_1.jsxs)("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item crisp-tabletoolbar-menu-item--danger", onClick: () => removeFilter(cond.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 16, strokeWidth: 1.75 }), deleteFilterLabel] }) })), fieldOpen && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabletoolbar-menu", role: "dialog", "aria-label": filterFieldLabel, children: nestedFieldPicker(filterColumns, (key) => changeFilterField(cond.id, key), filterFieldPlaceholder, filterFieldLabel) })), opOpen && ((0, jsx_runtime_1.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) => ((0, jsx_runtime_1.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 ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "crisp-tabletoolbar-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))) })), valOpen && showValueSeg && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--val", role: "dialog", "aria-label": `${col?.label} filter value`, children: cond.op === "isanyof" && col?.values ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("input", { className: "crisp-tabletoolbar-menu-input", placeholder: filterValuePlaceholder, "aria-label": `${col?.label} filter values`,
856
881
  // biome-ignore lint/a11y/noAutofocus: focus the search field
857
882
  autoFocus: true, value: anyOfQuery, onChange: (e) => setAnyOfQuery(e.target.value) }), col.values
858
883
  .filter((v) => v
@@ -46,7 +46,7 @@ const tabs_keyboard_1 = require("./tabs-keyboard");
46
46
  * Home/End jump); activation follows focus. Controlled via
47
47
  * `value`/`onValueChange`, or uncontrolled from `defaultValue`.
48
48
  */
49
- function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, moreLabel = (n) => `+${n} more`, moreMenuLabel = "More tabs", className, fill, "aria-label": ariaLabel, ...rest }) {
49
+ function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, moreLabel = (n) => `+${n} more`, moreMenuLabel = "More tabs", overflow = "collapse", className, fill, "aria-label": ariaLabel, ...rest }) {
50
50
  // Scoped to this instance: `tab-${value}` and `panel-${value}` are global,
51
51
  // so two tab bars sharing a value mint the same id twice and every
52
52
  // aria-labelledby resolves to the first one (#227).
@@ -93,6 +93,12 @@ function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, mor
93
93
  const [hiddenFrom, setHiddenFrom] = React.useState(null);
94
94
  const moreRef = React.useRef(null);
95
95
  React.useLayoutEffect(() => {
96
+ // crisp#908: "scroll" never collapses anything -- the row scrolls
97
+ // instead, so there is nothing to measure or hide.
98
+ if (overflow !== "collapse") {
99
+ setHiddenFrom(null);
100
+ return;
101
+ }
96
102
  const list = listRef.current;
97
103
  if (!list || tabs.length === 0)
98
104
  return;
@@ -133,7 +139,7 @@ function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, mor
133
139
  const ro = new ResizeObserver(recompute);
134
140
  ro.observe(list);
135
141
  return () => ro.disconnect();
136
- }, [tabs, active]);
142
+ }, [tabs, active, overflow]);
137
143
  const hiddenTabs = hiddenFrom == null ? [] : tabs.slice(hiddenFrom);
138
144
  const { onKeyDown, tabIndexFor } = (0, tabs_keyboard_1.useTablistKeyboard)({
139
145
  tabs,
@@ -141,11 +147,11 @@ function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, mor
141
147
  select,
142
148
  listRef,
143
149
  });
144
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, tabs_recipe_1.tabsRecipe)({ fill, className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { ref: listRef, className: "crisp-tabs-list", role: "tablist", "aria-label": ariaLabel, onKeyDown: onKeyDown, children: [leading && (0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-leading", children: leading }), tabs.map((t, i) => {
150
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, tabs_recipe_1.tabsRecipe)({ fill, className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { ref: listRef, className: "crisp-tabs-list", "data-overflow": overflow, role: "tablist", "aria-label": ariaLabel, onKeyDown: onKeyDown, children: [leading && (0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-leading", children: leading }), tabs.map((t, i) => {
145
151
  const selected = t.value === active;
146
152
  const overflowHidden = hiddenFrom != null && i >= hiddenFrom;
147
153
  return ((0, jsx_runtime_1.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, (0, jsx_runtime_1.jsx)("span", { className: "tabs-tab-label", children: t.label }), t.badge != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value));
148
- }), (0, jsx_runtime_1.jsx)("span", { ref: moreRef, className: "crisp-tabs-more", "data-tabs-overflow-hidden": hiddenTabs.length === 0 ? "" : undefined, children: (0, jsx_runtime_1.jsx)(menu_1.Menu, { "aria-label": moreMenuLabel, trigger: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-tabs-tab", children: [(0, jsx_runtime_1.jsx)("span", { className: "tabs-tab-label", children: moreLabel(hiddenTabs.length) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" })] }), children: hiddenTabs.map((t) => ((0, jsx_runtime_1.jsxs)(menu_1.MenuItem, { icon: t.icon, onSelect: () => select(t.value), children: [t.label, t.badge != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value))) }) }), indicator && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-indicator", style: {
154
+ }), overflow === "collapse" && ((0, jsx_runtime_1.jsx)("span", { ref: moreRef, className: "crisp-tabs-more", "data-tabs-overflow-hidden": hiddenTabs.length === 0 ? "" : undefined, children: (0, jsx_runtime_1.jsx)(menu_1.Menu, { "aria-label": moreMenuLabel, trigger: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-tabs-tab", children: [(0, jsx_runtime_1.jsx)("span", { className: "tabs-tab-label", children: moreLabel(hiddenTabs.length) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" })] }), children: hiddenTabs.map((t) => ((0, jsx_runtime_1.jsxs)(menu_1.MenuItem, { icon: t.icon, onSelect: () => select(t.value), children: [t.label, t.badge != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value))) }) })), indicator && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-indicator", style: {
149
155
  width: indicator.width,
150
156
  transform: `translateX(${indicator.left}px)`,
151
157
  } }))] }), children && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabs-panel", role: "tabpanel", id: panelId(active), "aria-labelledby": tabId(active),
@@ -8,10 +8,20 @@ const tokens_1 = require("../../tokens");
8
8
  // baseline needs, reserved as bottom padding so it's real box height and not
9
9
  // paint that trailing content can sit on top of. The bar carries 12px inline
10
10
  // padding too, and draws its own full-width `::after` baseline in
11
- // borderSubtle at its bottom edge, spanning that padding. The active tab is
12
- // a light pill — bg #fbfbfb + inset ring borderSubtle, r8 — with a 1px ink
13
- // underline that sits on the baseline (painted above it) and slides along it;
14
- // inactive tabs are muted and fill to a subtle pill on hover. Each tab carries
11
+ // borderSubtle at its bottom edge, spanning that padding.
12
+ //
13
+ // your-moon/crisp#934: the selected tab used to be a light
14
+ // filled pill (bg #fbfbfb + inset ring borderSubtle, r8) with a 1px ink
15
+ // underline on top of it. Owner-directed: underline tabs, no fill pill --
16
+ // seed-design's own `tab.yaml` has no `root.color` property at all, at any
17
+ // state (selected/hover/pressed); selection is carried purely by the label
18
+ // darkening (muted -> ink, unchanged, already matched seed) plus the
19
+ // existing underline (already `fgPrimary`, "same colour as selected text,
20
+ // not brand" -- also already matched seed). Only the selected pill's own
21
+ // fill/ring is removed here; unselected tabs keep their existing subtle
22
+ // hover tint (a desktop hover affordance seed's spec doesn't need to
23
+ // consider one way or the other, and the owner didn't ask to remove).
24
+ // Inactive tabs are muted and fill to a subtle pill on hover. Each tab carries
15
25
  // a 16px leading icon and an optional count badge. Measured from the live
16
26
  // app: bar `padding: 0 12px 8px`, 28px pills, 4/8 padding, 6px icon-label
17
27
  // gap, 14/500 labels. The count badge (crisp#697, re-measured against a
@@ -48,11 +58,18 @@ const config = (0, core_1.defineRecipe)({
48
58
 
49
59
  & .crisp-tabs-list { position: relative; display: flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; box-sizing: content-box; height: ${tokens_1.contract.size.row}; padding: 0 ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["2"]}; flex-shrink: 0; }
50
60
  & .crisp-tabs-list::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: ${tokens_1.contract.borderSubtle}; }
61
+ /* crisp#908: an alternative to the "+N more" collapse above (crisp#863) --
62
+ a consumer opts into a single scrolling row instead (Naver/Kakao's own
63
+ pattern, docs/study/korean/), never both at once. No new visual value:
64
+ reuses the same list gap/tab geometry, only adds overflow-x + snap. */
65
+ & .crisp-tabs-list[data-overflow="scroll"] { overflow-x: auto; flex-wrap: nowrap; scroll-snap-type: x mandatory; scrollbar-width: none; }
66
+ & .crisp-tabs-list[data-overflow="scroll"]::-webkit-scrollbar { display: none; }
67
+ & .crisp-tabs-list[data-overflow="scroll"] .crisp-tabs-tab { flex-shrink: 0; scroll-snap-align: start; }
51
68
  & .crisp-tabs-leading { display: inline-flex; align-items: center; margin-inline-end: ${tokens_1.contract.space["1"]}; }
52
- & .crisp-tabs-tab { position: relative; display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; height: 28px; margin: 0; padding: 0 ${tokens_1.contract.space["2"]}; border: none; background: none; box-shadow: inset 0 0 0 1px transparent; border-radius: ${tokens_1.contract.radius.md}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; letter-spacing: -0.14px; color: ${tokens_1.contract.fgMuted}; cursor: pointer; white-space: nowrap; transition: color .12s ease, background-color .12s ease, box-shadow .12s ease; }
69
+ & .crisp-tabs-tab { position: relative; display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; height: 28px; margin: 0; padding: 0 ${tokens_1.contract.space["2"]}; border: none; background: none; border-radius: ${tokens_1.contract.radius.md}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; letter-spacing: -0.14px; color: ${tokens_1.contract.fgMuted}; cursor: pointer; white-space: nowrap; transition: color .12s ease, background-color .12s ease; }
53
70
  & .crisp-tabs-tab svg { flex-shrink: 0; opacity: 0.85; }
54
71
  & .crisp-tabs-tab:hover:not([data-selected]) { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, transparent); color: ${tokens_1.contract.fgPrimary}; }
55
- & .crisp-tabs-tab[data-selected] { color: ${tokens_1.contract.fgPrimary}; background: ${tokens_1.contract.bgSubtle}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderSubtle}; }
72
+ & .crisp-tabs-tab[data-selected] { color: ${tokens_1.contract.fgPrimary}; }
56
73
  & .crisp-tabs-tab:focus-visible { outline: 2px solid ${tokens_1.contract.bgBrandSolid}; outline-offset: 1px; }
57
74
  & .crisp-tabs-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 14px; height: 14px; padding: 0 ${tokens_1.contract.space["1"]}; border-radius: 4px; background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, transparent); box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderSubtle}; font-size: 10px; line-height: 14px; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgNeutralSoft}; }
58
75
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: ${tokens_1.contract.fgPrimary}; }
@@ -3,14 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.textareaRecipe = void 0;
4
4
  const core_1 = require("../../core");
5
5
  const tokens_1 = require("../../tokens");
6
- // A multiline text field: an inset 1px ring (radius 8), 14px text, and an
6
+ // A multiline text field: a real 1px border (radius 8), 14px text, and an
7
7
  // optional character counter pinned bottom-right (12px, muted). Focus rings
8
8
  // the field brand.
9
9
  //
10
- // The ring is `elevation.textField` (#466), not `elevation.field` — this
11
- // comment already correctly described "an inset 1px ring", but the code
12
- // pulled in `field`'s own button-chrome shadow (a ring PLUS a glow and a
13
- // drop-shadow layer), which live Attio never carries on a real typed field.
10
+ // your-moon/crisp#920: the rest-state edge used to be `elevation.textField`,
11
+ // a `box-shadow` ring (#466's fix for a real typed field's 1px ring, not
12
+ // `field`'s button-chrome glow/drop-shadow layers) -- now a real `border`,
13
+ // matching Input's own #914/#910 technique and seed-design's `text-input.yaml`
14
+ // (an actual border that THICKENS on focus/invalid via the ::after overlay
15
+ // below, not a fixed-width ring that only recolors). `borderDefault` is
16
+ // seed's own `stroke.neutral-weak`, already the crisp token's exact value.
14
17
  //
15
18
  // The inset is seed-design's, not Attio's -- the second deliberate divergence
16
19
  // after the focus treatment. seed's text-input sets its inline inset to
@@ -27,10 +30,10 @@ const config = (0, core_1.defineRecipe)({
27
30
 
28
31
  & .crisp-textarea-field {
29
32
  box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical;
30
- margin: 0; padding: ${tokens_1.contract.space["2.5"]} 14px; border: none; border-radius: ${tokens_1.contract.radius.md};
31
- background: ${tokens_1.contract.bgSurface}; box-shadow: ${tokens_1.elevation.textField};
33
+ margin: 0; padding: ${tokens_1.contract.space["2.5"]} 14px; border-radius: ${tokens_1.contract.radius.md};
34
+ background: ${tokens_1.contract.bgSurface}; border: 1px solid ${tokens_1.contract.borderDefault};
32
35
  font-family: inherit; font-size: ${tokens_1.contract.text.lg}; line-height: 20px; color: ${tokens_1.contract.fgPrimary};
33
- outline: none; transition: box-shadow .15s ease;
36
+ outline: none;
34
37
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
35
38
  }
36
39
  & .crisp-textarea-field::placeholder { color: ${tokens_1.contract.fgMuted}; }
@@ -52,10 +52,11 @@ const react_dom_1 = require("react-dom");
52
52
  * `Dialog` was rendering offset by the dialog's own box instead of anchored
53
53
  * to its trigger.
54
54
  *
55
- * Every caller renders this conditionally on its own `open` state, which
56
- * starts `false` and is driven only by client interaction — never a prop a
57
- * fixture sets true at mount (unlike Dialog/Drawer/Command) — so this never
58
- * touches `document` during SSR, the same safety Tooltip already had.
55
+ * Every caller renders this conditionally on its own `open` state. A fixture
56
+ * CAN set that true at mount (crisp#964 needs exactly that, to give Menu's
57
+ * roving-tabindex guard something to inspect), so this checks for `window`
58
+ * itself rather than assuming the caller never does — the same guard
59
+ * `@zag-js/react`'s own `Portal` uses for the identical reason.
59
60
  *
60
61
  * `sourceRef` is the caller's own root — still in its original DOM position,
61
62
  * never portaled — used to carry a `data-theme` set on some ancestor of it
@@ -71,6 +72,8 @@ const react_dom_1 = require("react-dom");
71
72
  * element carrying that attribute.
72
73
  */
73
74
  function FloatingPortal({ children, sourceRef, }) {
75
+ if (typeof window === "undefined")
76
+ return children;
74
77
  const theme = sourceRef.current
75
78
  ?.closest("[data-theme]")
76
79
  ?.getAttribute("data-theme");
package/dist/cjs/index.js CHANGED
@@ -24,6 +24,7 @@ __exportStar(require("./components/avatar"), exports);
24
24
  __exportStar(require("./components/avatargroup"), exports);
25
25
  __exportStar(require("./components/badge"), exports);
26
26
  __exportStar(require("./components/barchart"), exports);
27
+ __exportStar(require("./components/bottomsheet"), exports);
27
28
  __exportStar(require("./components/breadcrumb"), exports);
28
29
  __exportStar(require("./components/bulkbar"), exports);
29
30
  __exportStar(require("./components/button"), exports);
@@ -31,6 +32,7 @@ __exportStar(require("./components/callout"), exports);
31
32
  __exportStar(require("./components/card"), exports);
32
33
  __exportStar(require("./components/checkbox"), exports);
33
34
  __exportStar(require("./components/chip"), exports);
35
+ __exportStar(require("./components/chipgroup"), exports);
34
36
  __exportStar(require("./components/columncalc"), exports);
35
37
  __exportStar(require("./components/composewindow"), exports);
36
38
  __exportStar(require("./components/contextmenu"), exports);
@@ -52,6 +54,7 @@ __exportStar(require("./components/kbd"), exports);
52
54
  __exportStar(require("./components/linechart"), exports);
53
55
  __exportStar(require("./components/mentionchip"), exports);
54
56
  __exportStar(require("./components/menu"), exports);
57
+ __exportStar(require("./components/menusheet"), exports);
55
58
  __exportStar(require("./components/middletruncate"), exports);
56
59
  __exportStar(require("./components/multiselect"), exports);
57
60
  __exportStar(require("./components/personchip"), exports);
@@ -65,6 +68,7 @@ __exportStar(require("./components/select"), exports);
65
68
  __exportStar(require("./components/skeleton"), exports);
66
69
  __exportStar(require("./components/skiplink"), exports);
67
70
  __exportStar(require("./components/slider"), exports);
71
+ __exportStar(require("./components/snackbar"), exports);
68
72
  __exportStar(require("./components/spinner"), exports);
69
73
  __exportStar(require("./components/stagebar"), exports);
70
74
  __exportStar(require("./components/statcard"), exports);
@@ -99,7 +103,9 @@ __exportStar(require("./product/headerbar"), exports);
99
103
  __exportStar(require("./product/importdialog"), exports);
100
104
  __exportStar(require("./product/inbox"), exports);
101
105
  __exportStar(require("./product/listpage"), exports);
106
+ __exportStar(require("./product/memberssettings"), exports);
102
107
  __exportStar(require("./product/navitem"), exports);
108
+ __exportStar(require("./product/notificationssettings"), exports);
103
109
  __exportStar(require("./product/objectsettings"), exports);
104
110
  __exportStar(require("./product/onboarding"), exports);
105
111
  __exportStar(require("./product/recordpage"), exports);
@@ -114,5 +120,6 @@ __exportStar(require("./product/tasklist"), exports);
114
120
  __exportStar(require("./product/themepicker"), exports);
115
121
  __exportStar(require("./product/trialbanner"), exports);
116
122
  __exportStar(require("./product/viewtoggle"), exports);
123
+ __exportStar(require("./product/workspacegeneralsettings"), exports);
117
124
  // design tokens + recipe core (for building custom components)
118
125
  __exportStar(require("./tokens"), exports);
@@ -43,7 +43,11 @@ const config = (0, core_1.defineRecipe)({
43
43
  & .crisp-authshell-progress-fill { height: 2px; background: ${tokens_1.contract.fgPrimary}; border-radius: 4px; transition: width .2s; }
44
44
 
45
45
  & .crisp-authshell-logo { margin-top: ${tokens_1.contract.space["8"]}; min-height: 24px; display: flex; align-items: center; gap: ${tokens_1.contract.space["2.5"]}; }
46
- & .crisp-authshell-logo-badge { flex-shrink: 0; box-sizing: border-box; width: 32px; height: 32px; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgBrandSolid}; color: #fff; display: flex; align-items: center; justify-content: center; }
46
+ /* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
47
+ the accent (#0298d9) only clears 3.23:1 with white; the darker
48
+ sibling clears 4.5:1+, same fix already applied to Button/Badge/
49
+ Avatar (#897). */
50
+ & .crisp-authshell-logo-badge { flex-shrink: 0; box-sizing: border-box; width: 32px; height: 32px; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgBrandContrast}; color: #fff; display: flex; align-items: center; justify-content: center; }
47
51
  & .crisp-authshell-logo-text { display: flex; flex-direction: column; line-height: 1.2; }
48
52
  & .crisp-authshell-logo-title { font-size: ${tokens_1.contract.text.xl}; font-weight: ${tokens_1.contract.weight.bold}; letter-spacing: -0.2px; color: ${tokens_1.contract.fgPrimary}; }
49
53
  & .crisp-authshell-logo-subtitle { font-size: ${tokens_1.contract.text.sm}; color: ${tokens_1.contract.fgMuted}; }
@@ -32,7 +32,10 @@ const config = (0, core_1.defineRecipe)({
32
32
  & .crisp-command-list > .crisp-command-group-label:first-child { padding-top: ${tokens_1.contract.space["1"]}; }
33
33
  & .crisp-command-footer { display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; height: 44px; padding: 0 ${tokens_1.contract.space["2.5"]}; border-top: 1px solid ${tokens_1.contract.borderSubtle}; }
34
34
  & .crisp-command-footer-nav { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; font-size: ${tokens_1.contract.text.sm}; color: ${tokens_1.contract.fgMuted}; }
35
- & .crisp-command-footer-action { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: 28px; max-width: 60%; padding: 0 ${tokens_1.contract.space["1"]} 0 ${tokens_1.contract.space["2.5"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgBrandSolid}; color: ${tokens_1.contract.fgOnBrand}; font-family: inherit; font-size: ${tokens_1.contract.text.md}; font-weight: ${tokens_1.contract.weight.medium}; cursor: pointer; }
35
+ /* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
36
+ the accent (#0298d9) only clears 3.23:1 with white; the darker sibling
37
+ clears 4.5:1+, same fix already applied to Button/Badge/Avatar (#897). */
38
+ & .crisp-command-footer-action { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: 28px; max-width: 60%; padding: 0 ${tokens_1.contract.space["1"]} 0 ${tokens_1.contract.space["2.5"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgBrandContrast}; color: ${tokens_1.contract.fgOnBrand}; font-family: inherit; font-size: ${tokens_1.contract.text.md}; font-weight: ${tokens_1.contract.weight.medium}; cursor: pointer; }
36
39
  & .crisp-command-footer-action:disabled { opacity: 0.5; cursor: default; }
37
40
  & .crisp-command-footer-action-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
38
41
  & .crisp-command-footer-key { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 ${tokens_1.contract.space["1"]}; border-radius: ${tokens_1.contract.radius.sm}; background: rgba(255,255,255,0.22); font-size: ${tokens_1.contract.text.xs}; }
@@ -315,8 +315,13 @@ function CommentRow({ comment, replyLabel, reactLabel, resolveLabel, moreLabel,
315
315
  * nothing on its own beyond that data. See `CommentCountBadge` for the
316
316
  * separate row affordance (P-051) — deliberately NOT part of this
317
317
  * component, since it lives in a table cell this component doesn't own.
318
+ *
319
+ * `variant="drawer"` (default) is today's measured 320px + left-border
320
+ * look; `variant="pane"` (crisp#960) fills its container's width instead,
321
+ * for a consumer embedding this in a full-width tab pane rather than a
322
+ * true slide-over/drawer.
318
323
  */
319
- 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 }) {
324
+ 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 }) {
320
325
  const closeLabel = (0, core_1.useCrispMessage)(closeLabelProp, "Close");
321
326
  const backLabel = (0, core_1.useCrispMessage)(backLabelProp, "Back");
322
327
  const title = (0, core_1.useCrispMessage)(titleProp, "All comments");
@@ -369,7 +374,7 @@ function CommentsPanel({ threads, currentUserId, subject, subjectIcon, showResol
369
374
  const activeThread = activeThreadId
370
375
  ? threads.find((t) => t.id === activeThreadId)
371
376
  : null;
372
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, comments_recipe_1.commentsRecipe)({ className }), role: "dialog", "aria-label": title, ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-comments-head", children: [activeThread ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": backLabel, onClick: () => setActiveThreadId(null), children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronLeft, { "data-rtl-flip": "", size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) })) : null, (0, jsx_runtime_1.jsxs)("div", { className: "crisp-comments-title-wrap", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-comments-title", children: activeThread ? threadTitle : title }), activeThread && ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-comments-breadcrumb", children: [breadcrumbPrefix, " ", subjectIcon, " ", subject] }))] }), !activeThread && ((0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "end", trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": filterLabel, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuCheckboxItem, { checked: showResolved, onCheckedChange: onShowResolvedChange, children: showResolvedLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuCheckboxItem, { checked: onlyMine, onCheckedChange: onOnlyMineChange, children: onlyMineLabel })] })), onClose && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": closeLabel, onClick: onClose, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }))] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-comments-body crisp-comments-body--pad-top", children: activeThread ? ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.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
377
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, comments_recipe_1.commentsRecipe)({ variant, className }), role: "dialog", "aria-label": title, ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-comments-head", children: [activeThread ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": backLabel, onClick: () => setActiveThreadId(null), children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronLeft, { "data-rtl-flip": "", size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) })) : null, (0, jsx_runtime_1.jsxs)("div", { className: "crisp-comments-title-wrap", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-comments-title", children: activeThread ? threadTitle : title }), activeThread && ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-comments-breadcrumb", children: [breadcrumbPrefix, " ", subjectIcon, " ", subject] }))] }), !activeThread && ((0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "end", trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": filterLabel, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuCheckboxItem, { checked: showResolved, onCheckedChange: onShowResolvedChange, children: showResolvedLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuCheckboxItem, { checked: onlyMine, onCheckedChange: onOnlyMineChange, children: onlyMineLabel })] })), onClose && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": closeLabel, onClick: onClose, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }))] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-comments-body crisp-comments-body--pad-top", children: activeThread ? ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.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
373
378
  ? activeThread.resolvedBy?.id === currentUserId ||
374
379
  !activeThread.resolvedBy
375
380
  ? resolvedByYouLabel
@@ -18,8 +18,8 @@ const tokens_1 = require("../../tokens");
18
18
  // width and still gets an odd number).
19
19
  const config = (0, core_1.defineRecipe)({
20
20
  name: "comments",
21
- base: `display: flex; flex-direction: column; box-sizing: border-box; width: 320px; height: 100%;
22
- background: ${tokens_1.contract.bgSurface}; border-inline-start: 1px solid ${tokens_1.contract.borderSubtle};
21
+ base: `display: flex; flex-direction: column; box-sizing: border-box; height: 100%;
22
+ background: ${tokens_1.contract.bgSurface};
23
23
  font-family: ${tokens_1.contract.font.sans};
24
24
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
25
25
  & * { box-sizing: border-box; }
@@ -99,9 +99,13 @@ const config = (0, core_1.defineRecipe)({
99
99
  box-shadow, so this reuses that literal rather than re-deriving it.
100
100
  32px/radius.lg/6px-10px padding has no matching Button \`size\` yet
101
101
  (24/28/40 are the three that exist), so this stays hand-rolled instead
102
- of stretching one of those. */
103
- & .crisp-comments-post { display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 6px ${tokens_1.contract.space["2.5"]}; border: none; border-radius: ${tokens_1.contract.radius.lg}; background: ${tokens_1.contract.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: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.semibold}; color: ${tokens_1.contract.fgOnBrand}; cursor: pointer; opacity: 1; transition: opacity .12s, background-color .12s; }
104
- & .crisp-comments-post:hover:not(:disabled) { background: ${tokens_1.contract.bgBrandSolidPressed}; }
102
+ of stretching one of those. your-moon/crisp#953:
103
+ bgBrandContrast, not bgBrandSolid -- the accent (#0298d9) only clears
104
+ 3.23:1 with white; same fix already applied to Button/Badge/Avatar
105
+ (#897). Hover is a brightness filter on the new fill, matching that
106
+ PR's own Button primary treatment, not bgBrandSolidPressed. */
107
+ & .crisp-comments-post { display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 6px ${tokens_1.contract.space["2.5"]}; border: none; border-radius: ${tokens_1.contract.radius.lg}; background: ${tokens_1.contract.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: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.semibold}; color: ${tokens_1.contract.fgOnBrand}; cursor: pointer; opacity: 1; transition: opacity .12s, filter .12s; }
108
+ & .crisp-comments-post:hover:not(:disabled) { filter: brightness(0.96); }
105
109
  & .crisp-comments-post:disabled { opacity: .4; cursor: default; }
106
110
 
107
111
  /* Filter menu -- measured: 220px, radius 12 (radius.xl), 4px padding,
@@ -113,8 +117,22 @@ const config = (0, core_1.defineRecipe)({
113
117
  rather than re-derived since it's the same atomic-chip affordance. */
114
118
  & .crisp-comments-mention { display: inline; padding: 0 2px; border-radius: 4px; background: color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 12%, transparent); color: ${tokens_1.contract.bgBrandSolid}; font-weight: ${tokens_1.contract.weight.medium}; user-select: none; }
115
119
  ${core_1.rtlFlipCss}`,
116
- variants: {},
117
- defaultVariants: {},
120
+ variants: {
121
+ // your-moon/crisp#960: the 320px width + left-border hairline is a
122
+ // drawer-consumer look (RecordPage's own comments drawer, #864), not
123
+ // an intrinsic property of the panel itself. A consumer embedding this
124
+ // in a full-width tab pane (internal/grc's own record-page Comments
125
+ // tab, sibling to Related/Activity/Notes/Tasks/Lists, all of which
126
+ // fill the pane) needs `width: 100%` and no border -- a tab pane
127
+ // doesn't need a seam separating it from itself. `drawer` (default)
128
+ // is today's exact, already-measured look; nothing changes for an
129
+ // existing consumer that doesn't pass `variant`.
130
+ variant: {
131
+ drawer: `width: 320px; border-inline-start: 1px solid ${tokens_1.contract.borderSubtle};`,
132
+ pane: `width: 100%;`,
133
+ },
134
+ },
135
+ defaultVariants: { variant: "drawer" },
118
136
  compoundVariants: [],
119
137
  // Two pieces below intentionally live OUTSIDE the nested \`base\` above,
120
138
  // the same reason Menu's own \`.crisp-menu-content\` does (menu.recipe.ts's
@@ -54,10 +54,18 @@ function Inbox({ notifications = [], title: titleProp, tabs, activeTab, onTabCha
54
54
  // own `empty`.
55
55
  const crispMessages = (0, core_1.useCrispMessages)();
56
56
  const title = titleProp ?? crispMessages.Notifications ?? "Notifications";
57
- const emptyTitle = emptyTitleProp ?? crispMessages["No notifications"] ?? "No notifications";
58
- const emptySubtitle = emptySubtitleProp ??
57
+ // A tab's own empty copy wins over the instance-level prop, which wins
58
+ // over the built-in default — same precedence as `title`/`emptyAction`.
59
+ const activeTabDef = tabs?.find((t) => t.id === activeTab);
60
+ const emptyTitle = activeTabDef?.emptyTitle ??
61
+ emptyTitleProp ??
62
+ crispMessages["No notifications"] ??
63
+ "No notifications";
64
+ const emptySubtitle = activeTabDef?.emptySubtitle ??
65
+ emptySubtitleProp ??
59
66
  crispMessages["Notifications such as mentions and important updates will be displayed here."] ??
60
67
  "Notifications such as mentions and important updates will be displayed here.";
68
+ const resolvedEmptyAction = activeTabDef?.emptyAction ?? emptyAction;
61
69
  const tabsLabel = (0, core_1.useCrispMessage)(tabsLabelProp, "Notification views");
62
70
  // crisp's shared tablist model, the one `Tabs` runs (#195). Hand-rolling a
63
71
  // second one is how Inbox ended up with no arrow keys, no roving tabindex
@@ -68,6 +76,6 @@ function Inbox({ notifications = [], title: titleProp, tabs, activeTab, onTabCha
68
76
  select: (id) => onTabChange?.(id),
69
77
  listRef,
70
78
  });
71
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, inbox_recipe_1.inboxRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-inbox-head", children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-inbox-title", children: title }), tabs && tabs.length > 0 && ((0, jsx_runtime_1.jsx)("div", { ref: listRef, className: "crisp-inbox-tabs", role: "tablist", "aria-label": tabsLabel, onKeyDown: onKeyDown, children: tabs.map((t) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", role: "tab", "aria-selected": t.id === activeTab, tabIndex: tabIndexFor(t.id), "data-value": t.id, disabled: t.disabled, className: "crisp-inbox-tab", "data-active": t.id === activeTab ? "" : undefined, onClick: () => onTabChange?.(t.id), children: [t.label, t.count != null && ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-inbox-tab-count", children: ["(", t.count, ")"] }))] }, t.id))) }))] }), notifications.length === 0 ? ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-inbox-empty", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-inbox-empty-icon", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Bell, { size: 40, strokeWidth: 1.25 }) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-inbox-empty-title", children: emptyTitle }), emptySubtitle != null && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-inbox-empty-sub", children: emptySubtitle })), emptyAction != null && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-inbox-empty-action", children: emptyAction }))] })) : ((0, jsx_runtime_1.jsx)("div", { className: "crisp-inbox-list", children: notifications.map((n) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-inbox-row", onClick: n.onClick, children: [n.avatar != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-inbox-row-avatar", children: n.avatar })), (0, jsx_runtime_1.jsxs)("span", { className: "crisp-inbox-row-body", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-inbox-row-msg", children: n.message }), n.time != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-inbox-row-time", children: n.time }))] }), n.unread && (0, jsx_runtime_1.jsx)("span", { className: "crisp-inbox-row-unread" })] }, n.id))) }))] }));
79
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, inbox_recipe_1.inboxRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-inbox-head", children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-inbox-title", children: title }), tabs && tabs.length > 0 && ((0, jsx_runtime_1.jsx)("div", { ref: listRef, className: "crisp-inbox-tabs", role: "tablist", "aria-label": tabsLabel, onKeyDown: onKeyDown, children: tabs.map((t) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", role: "tab", "aria-selected": t.id === activeTab, tabIndex: tabIndexFor(t.id), "data-value": t.id, disabled: t.disabled, className: "crisp-inbox-tab", "data-active": t.id === activeTab ? "" : undefined, onClick: () => onTabChange?.(t.id), children: [t.label, t.count != null && ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-inbox-tab-count", children: ["(", t.count, ")"] }))] }, t.id))) }))] }), notifications.length === 0 ? ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-inbox-empty", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-inbox-empty-icon", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Bell, { size: 40, strokeWidth: 1.25 }) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-inbox-empty-title", children: emptyTitle }), emptySubtitle != null && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-inbox-empty-sub", children: emptySubtitle })), resolvedEmptyAction != null && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-inbox-empty-action", children: resolvedEmptyAction }))] })) : ((0, jsx_runtime_1.jsx)("div", { className: "crisp-inbox-list", children: notifications.map((n) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-inbox-row", onClick: n.onClick, children: [n.avatar != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-inbox-row-avatar", children: n.avatar })), (0, jsx_runtime_1.jsxs)("span", { className: "crisp-inbox-row-body", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-inbox-row-msg", children: n.message }), n.time != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-inbox-row-time", children: n.time }))] }), n.unread && (0, jsx_runtime_1.jsx)("span", { className: "crisp-inbox-row-unread" })] }, n.id))) }))] }));
72
80
  }
73
81
  Inbox.displayName = "Inbox";
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MembersSettings = exports.InviteMembersDialog = void 0;
4
+ var memberssettings_1 = require("./memberssettings");
5
+ Object.defineProperty(exports, "InviteMembersDialog", { enumerable: true, get: function () { return memberssettings_1.InviteMembersDialog; } });
6
+ Object.defineProperty(exports, "MembersSettings", { enumerable: true, get: function () { return memberssettings_1.MembersSettings; } });