@crisp-ui-kit/crisp 0.49.0 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ const tokens_1 = require("../../tokens");
10
10
  const config = (0, core_1.defineRecipe)({
11
11
  name: "datatable",
12
12
  base: `display: flex; flex-direction: column; gap: ${tokens_1.contract.space["2.5"]}; font-family: ${tokens_1.contract.font.sans};
13
- & .crisp-datatable-toolbar { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; }
13
+ & .crisp-datatable-toolbar { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; min-height: 49px; border-top: 1px solid ${tokens_1.contract.borderSubtle}; }
14
14
  & .crisp-datatable-search { position: relative; flex: 1; max-width: 280px; display: inline-flex; align-items: center; }
15
15
  & .crisp-datatable-search svg { position: absolute; inset-inline-start: ${tokens_1.contract.space["2"]}; color: ${tokens_1.contract.fgMuted}; pointer-events: none; }
16
16
  & .crisp-datatable-search input { width: 100%; height: 32px; padding: 0 ${tokens_1.contract.space["2"]} 0 ${tokens_1.contract.space["6"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgSurface}; box-shadow: ${tokens_1.elevation.field}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; color: ${tokens_1.contract.fgPrimary}; outline: none; transition: box-shadow .15s; }
@@ -18,6 +18,7 @@ const tokens_1 = require("../../tokens");
18
18
  // split preserves that cascade exactly, which is what lets the generated CSS
19
19
  // stay byte-for-byte identical to what shipped before the extraction.
20
20
  const toolbarChromeHead = (p) => ` & .${namespace_1.CLASS_PREFIX}${p}-toolbar { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; padding: 0 ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["2"]}; }
21
+ & .${namespace_1.CLASS_PREFIX}${p}-toolbar[data-inline] { padding-bottom: 0; }
21
22
  & .${namespace_1.CLASS_PREFIX}${p}-search { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; 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.bgSurface}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderSubtle}; color: ${tokens_1.contract.fgMuted}; }
22
23
  & .${namespace_1.CLASS_PREFIX}${p}-search svg { flex-shrink: 0; }
23
24
  & .${namespace_1.CLASS_PREFIX}${p}-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: ${tokens_1.contract.text.md}; color: ${tokens_1.contract.fgPrimary}; width: 180px; }
@@ -33,6 +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
37
  exports.applyFilters = applyFilters;
37
38
  exports.applySorts = applySorts;
38
39
  exports.TableToolbar = TableToolbar;
@@ -149,7 +150,7 @@ function applySorts(rows, sorts, get = (row, key) => row[key]) {
149
150
  * composable version, to put above `DataTable` or any table of your own. Pair
150
151
  * it with `applyFilters` / `applySorts` to turn its state into rows.
151
152
  */
152
- function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel = "Sort", reorderSortLabel = "Reorder sort", noSortsLabel = "No sorts applied to this view.", addSortLabel = "Add sort", learnSortingLabel = "Learn about sorting", selectConditionLabel = "Select condition", sortFieldLabel = "Sort field", sortDirectionLabel = "Sort direction", removeSortLabel = "Remove sort", filterFieldLabel = "Filter by field", filterFieldPlaceholder = "Filter by…", filterValuePlaceholder = "Value…", ascendingLabel = "Ascending", descendingLabel = "Descending", sortTriggerLabel, filterTriggerLabel, ...rest }) {
153
+ function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel = "Sort", reorderSortLabel = "Reorder sort", noSortsLabel = "No sorts applied to this view.", addSortLabel = "Add sort", learnSortingLabel = "Learn about sorting", selectConditionLabel = "Select condition", sortFieldLabel = "Sort field", sortDirectionLabel = "Sort direction", removeSortLabel = "Remove sort", filterFieldLabel = "Filter by field", filterFieldPlaceholder = "Filter by…", filterValuePlaceholder = "Value…", ascendingLabel = "Ascending", descendingLabel = "Descending", sortTriggerLabel, filterTriggerLabel, inline, ...rest }) {
153
154
  const [sortsState, setSortsState] = React.useState(defaultSorts ?? []);
154
155
  const [filtersState, setFiltersState] = React.useState(defaultFilters ?? []);
155
156
  const sorts = sortsProp ?? sortsState;
@@ -239,7 +240,7 @@ function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange,
239
240
  : sorts.length
240
241
  ? `Sorted by ${columns.find((c) => c.key === sorts[0].key)?.label}${sorts.length > 1 ? ` +${sorts.length - 1}` : ""}`
241
242
  : "Sort";
242
- return ((0, jsx_runtime_1.jsx)("div", { className: (0, tabletoolbar_recipe_1.tabletoolbarRecipe)({ className }), ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-tabletoolbar-toolbar", children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-tabletoolbar-toolgroup", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: `crisp-tabletoolbar-tool${sorts.length ? " crisp-tabletoolbar-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sortedLabel] }), openMenu === "sort" && ((0, jsx_runtime_1.jsxs)("div", { ref: sortListRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-sortpop", role: "dialog", "aria-label": sortLabel, children: [sorts.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabletoolbar-sort-empty", children: noSortsLabel })) : (sorts.map((s, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-tabletoolbar-sort-row", onDragOver: (e) => {
243
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, tabletoolbar_recipe_1.tabletoolbarRecipe)({ className }), ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-tabletoolbar-toolbar", "data-inline": inline ? "" : undefined, children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-tabletoolbar-toolgroup", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: `crisp-tabletoolbar-tool${sorts.length ? " crisp-tabletoolbar-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sortedLabel] }), openMenu === "sort" && ((0, jsx_runtime_1.jsxs)("div", { ref: sortListRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-sortpop", role: "dialog", "aria-label": sortLabel, children: [sorts.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabletoolbar-sort-empty", children: noSortsLabel })) : (sorts.map((s, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-tabletoolbar-sort-row", onDragOver: (e) => {
243
244
  if (dragSort !== null)
244
245
  e.preventDefault();
245
246
  }, onDrop: () => {
@@ -301,3 +302,22 @@ function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange,
301
302
  })] }))] }))] }, cond.id));
302
303
  }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-tabletoolbar-spacer" }), count != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabletoolbar-count", children: count })), children] }) }));
303
304
  }
305
+ /**
306
+ * The Sort/Filter chip's own chrome (`.crisp-tabletoolbar-tool`), standalone
307
+ * (#399) — for a control drawn like Sort/Filter that isn't Sort or Filter (a
308
+ * screen's own quick filter, e.g.). `.crisp-tabletoolbar-tool` alone renders
309
+ * unstyled: the rule lives nested under `TableToolbar`'s own root class, the
310
+ * same way every other rule in its shared chrome does, so it only paints on
311
+ * a descendant of an actual `TableToolbar`. This wraps one in, `display:
312
+ * contents` so it contributes no box of its own — the same technique
313
+ * `TableToolbar`'s own root already uses to disappear from layout.
314
+ */
315
+ exports.ToolbarTool = React.forwardRef(({ active, ghost, className, ...rest }, ref) => ((0, jsx_runtime_1.jsx)("span", { className: (0, tabletoolbar_recipe_1.tabletoolbarRecipe)(), children: (0, jsx_runtime_1.jsx)("button", { ref: ref, type: "button", className: [
316
+ "crisp-tabletoolbar-tool",
317
+ active && "crisp-tabletoolbar-tool--active",
318
+ ghost && "crisp-tabletoolbar-tool--ghost",
319
+ className,
320
+ ]
321
+ .filter(Boolean)
322
+ .join(" "), ...rest }) })));
323
+ exports.ToolbarTool.displayName = "ToolbarTool";
@@ -1,6 +1,6 @@
1
1
  .crisp-datatable {
2
2
  display: flex; flex-direction: column; gap: var(--crisp-space-2-5); font-family: var(--crisp-font-sans);
3
- & .crisp-datatable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); }
3
+ & .crisp-datatable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); min-height: 49px; border-top: 1px solid var(--crisp-border-subtle); }
4
4
  & .crisp-datatable-search { position: relative; flex: 1; max-width: 280px; display: inline-flex; align-items: center; }
5
5
  & .crisp-datatable-search svg { position: absolute; inset-inline-start: var(--crisp-space-2); color: var(--crisp-fg-muted); pointer-events: none; }
6
6
  & .crisp-datatable-search input { width: 100%; height: 32px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-6); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; transition: box-shadow .15s; }
@@ -1,7 +1,7 @@
1
1
  @layer crisp-components {
2
2
  .crisp-datatable {
3
3
  display: flex; flex-direction: column; gap: var(--crisp-space-2-5); font-family: var(--crisp-font-sans);
4
- & .crisp-datatable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); }
4
+ & .crisp-datatable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); min-height: 49px; border-top: 1px solid var(--crisp-border-subtle); }
5
5
  & .crisp-datatable-search { position: relative; flex: 1; max-width: 280px; display: inline-flex; align-items: center; }
6
6
  & .crisp-datatable-search svg { position: absolute; inset-inline-start: var(--crisp-space-2); color: var(--crisp-fg-muted); pointer-events: none; }
7
7
  & .crisp-datatable-search input { width: 100%; height: 32px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-6); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; transition: box-shadow .15s; }
@@ -7,7 +7,7 @@ import { contract as c, elevation as e } from "../../tokens/index.js";
7
7
  const config = defineRecipe({
8
8
  name: "datatable",
9
9
  base: `display: flex; flex-direction: column; gap: ${c.space["2.5"]}; font-family: ${c.font.sans};
10
- & .crisp-datatable-toolbar { display: flex; align-items: center; gap: ${c.space["2"]}; }
10
+ & .crisp-datatable-toolbar { display: flex; align-items: center; gap: ${c.space["2"]}; min-height: 49px; border-top: 1px solid ${c.borderSubtle}; }
11
11
  & .crisp-datatable-search { position: relative; flex: 1; max-width: 280px; display: inline-flex; align-items: center; }
12
12
  & .crisp-datatable-search svg { position: absolute; inset-inline-start: ${c.space["2"]}; color: ${c.fgMuted}; pointer-events: none; }
13
13
  & .crisp-datatable-search input { width: 100%; height: 32px; padding: 0 ${c.space["2"]} 0 ${c.space["6"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgSurface}; box-shadow: ${e.field}; font-family: inherit; font-size: ${c.text.lg}; color: ${c.fgPrimary}; outline: none; transition: box-shadow .15s; }
@@ -15,6 +15,7 @@ import { contract as c, elevation as e } from "../../tokens/index.js";
15
15
  // split preserves that cascade exactly, which is what lets the generated CSS
16
16
  // stay byte-for-byte identical to what shipped before the extraction.
17
17
  export const toolbarChromeHead = (p) => ` & .${CLASS_PREFIX}${p}-toolbar { display: flex; align-items: center; gap: ${c.space["1.5"]}; padding: 0 ${c.space["3"]} ${c.space["2"]}; }
18
+ & .${CLASS_PREFIX}${p}-toolbar[data-inline] { padding-bottom: 0; }
18
19
  & .${CLASS_PREFIX}${p}-search { display: flex; align-items: center; gap: ${c.space["1.5"]}; height: ${c.size.row}; padding: 0 ${c.space["2.5"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgSurface}; box-shadow: inset 0 0 0 1px ${c.borderSubtle}; color: ${c.fgMuted}; }
19
20
  & .${CLASS_PREFIX}${p}-search svg { flex-shrink: 0; }
20
21
  & .${CLASS_PREFIX}${p}-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: ${c.text.md}; color: ${c.fgPrimary}; width: 180px; }
@@ -111,7 +111,7 @@ export function applySorts(rows, sorts, get = (row, key) => row[key]) {
111
111
  * composable version, to put above `DataTable` or any table of your own. Pair
112
112
  * it with `applyFilters` / `applySorts` to turn its state into rows.
113
113
  */
114
- export function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel = "Sort", reorderSortLabel = "Reorder sort", noSortsLabel = "No sorts applied to this view.", addSortLabel = "Add sort", learnSortingLabel = "Learn about sorting", selectConditionLabel = "Select condition", sortFieldLabel = "Sort field", sortDirectionLabel = "Sort direction", removeSortLabel = "Remove sort", filterFieldLabel = "Filter by field", filterFieldPlaceholder = "Filter by…", filterValuePlaceholder = "Value…", ascendingLabel = "Ascending", descendingLabel = "Descending", sortTriggerLabel, filterTriggerLabel, ...rest }) {
114
+ export function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel = "Sort", reorderSortLabel = "Reorder sort", noSortsLabel = "No sorts applied to this view.", addSortLabel = "Add sort", learnSortingLabel = "Learn about sorting", selectConditionLabel = "Select condition", sortFieldLabel = "Sort field", sortDirectionLabel = "Sort direction", removeSortLabel = "Remove sort", filterFieldLabel = "Filter by field", filterFieldPlaceholder = "Filter by…", filterValuePlaceholder = "Value…", ascendingLabel = "Ascending", descendingLabel = "Descending", sortTriggerLabel, filterTriggerLabel, inline, ...rest }) {
115
115
  const [sortsState, setSortsState] = React.useState(defaultSorts ?? []);
116
116
  const [filtersState, setFiltersState] = React.useState(defaultFilters ?? []);
117
117
  const sorts = sortsProp ?? sortsState;
@@ -201,7 +201,7 @@ export function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsC
201
201
  : sorts.length
202
202
  ? `Sorted by ${columns.find((c) => c.key === sorts[0].key)?.label}${sorts.length > 1 ? ` +${sorts.length - 1}` : ""}`
203
203
  : "Sort";
204
- return (_jsx("div", { className: tabletoolbarRecipe({ className }), ...rest, children: _jsxs("div", { className: "crisp-tabletoolbar-toolbar", children: [_jsxs("span", { className: "crisp-tabletoolbar-toolgroup", children: [_jsxs("button", { type: "button", className: `crisp-tabletoolbar-tool${sorts.length ? " crisp-tabletoolbar-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [_jsx(ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sortedLabel] }), openMenu === "sort" && (_jsxs("div", { ref: sortListRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-sortpop", role: "dialog", "aria-label": sortLabel, children: [sorts.length === 0 ? (_jsx("div", { className: "crisp-tabletoolbar-sort-empty", children: noSortsLabel })) : (sorts.map((s, i) => (_jsxs("div", { className: "crisp-tabletoolbar-sort-row", onDragOver: (e) => {
204
+ return (_jsx("div", { className: tabletoolbarRecipe({ className }), ...rest, children: _jsxs("div", { className: "crisp-tabletoolbar-toolbar", "data-inline": inline ? "" : undefined, children: [_jsxs("span", { className: "crisp-tabletoolbar-toolgroup", children: [_jsxs("button", { type: "button", className: `crisp-tabletoolbar-tool${sorts.length ? " crisp-tabletoolbar-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [_jsx(ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sortedLabel] }), openMenu === "sort" && (_jsxs("div", { ref: sortListRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-sortpop", role: "dialog", "aria-label": sortLabel, children: [sorts.length === 0 ? (_jsx("div", { className: "crisp-tabletoolbar-sort-empty", children: noSortsLabel })) : (sorts.map((s, i) => (_jsxs("div", { className: "crisp-tabletoolbar-sort-row", onDragOver: (e) => {
205
205
  if (dragSort !== null)
206
206
  e.preventDefault();
207
207
  }, onDrop: () => {
@@ -263,3 +263,22 @@ export function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsC
263
263
  })] }))] }))] }, cond.id));
264
264
  }), _jsx("span", { className: "crisp-tabletoolbar-spacer" }), count != null && (_jsx("span", { className: "crisp-tabletoolbar-count", children: count })), children] }) }));
265
265
  }
266
+ /**
267
+ * The Sort/Filter chip's own chrome (`.crisp-tabletoolbar-tool`), standalone
268
+ * (#399) — for a control drawn like Sort/Filter that isn't Sort or Filter (a
269
+ * screen's own quick filter, e.g.). `.crisp-tabletoolbar-tool` alone renders
270
+ * unstyled: the rule lives nested under `TableToolbar`'s own root class, the
271
+ * same way every other rule in its shared chrome does, so it only paints on
272
+ * a descendant of an actual `TableToolbar`. This wraps one in, `display:
273
+ * contents` so it contributes no box of its own — the same technique
274
+ * `TableToolbar`'s own root already uses to disappear from layout.
275
+ */
276
+ export const ToolbarTool = React.forwardRef(({ active, ghost, className, ...rest }, ref) => (_jsx("span", { className: tabletoolbarRecipe(), children: _jsx("button", { ref: ref, type: "button", className: [
277
+ "crisp-tabletoolbar-tool",
278
+ active && "crisp-tabletoolbar-tool--active",
279
+ ghost && "crisp-tabletoolbar-tool--ghost",
280
+ className,
281
+ ]
282
+ .filter(Boolean)
283
+ .join(" "), ...rest }) })));
284
+ ToolbarTool.displayName = "ToolbarTool";
@@ -3,6 +3,7 @@
3
3
  & * { box-sizing: border-box; }
4
4
  /* toolbar */
5
5
  & .crisp-recordstable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: 0 var(--crisp-space-3) var(--crisp-space-2); }
6
+ & .crisp-recordstable-toolbar[data-inline] { padding-bottom: 0; }
6
7
  & .crisp-recordstable-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); color: var(--crisp-fg-muted); }
7
8
  & .crisp-recordstable-search svg { flex-shrink: 0; }
8
9
  & .crisp-recordstable-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); width: 180px; }
@@ -4,6 +4,7 @@
4
4
  & * { box-sizing: border-box; }
5
5
  /* toolbar */
6
6
  & .crisp-recordstable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: 0 var(--crisp-space-3) var(--crisp-space-2); }
7
+ & .crisp-recordstable-toolbar[data-inline] { padding-bottom: 0; }
7
8
  & .crisp-recordstable-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); color: var(--crisp-fg-muted); }
8
9
  & .crisp-recordstable-search svg { flex-shrink: 0; }
9
10
  & .crisp-recordstable-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); width: 180px; }
package/dist/styles.css CHANGED
@@ -516,7 +516,7 @@
516
516
 
517
517
  .crisp-datatable {
518
518
  display: flex; flex-direction: column; gap: var(--crisp-space-2-5); font-family: var(--crisp-font-sans);
519
- & .crisp-datatable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); }
519
+ & .crisp-datatable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); min-height: 49px; border-top: 1px solid var(--crisp-border-subtle); }
520
520
  & .crisp-datatable-search { position: relative; flex: 1; max-width: 280px; display: inline-flex; align-items: center; }
521
521
  & .crisp-datatable-search svg { position: absolute; inset-inline-start: var(--crisp-space-2); color: var(--crisp-fg-muted); pointer-events: none; }
522
522
  & .crisp-datatable-search input { width: 100%; height: 32px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-6); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; transition: box-shadow .15s; }
@@ -1551,6 +1551,7 @@
1551
1551
  display: contents; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1552
1552
  & * { box-sizing: border-box; }
1553
1553
  & .crisp-tabletoolbar-toolbar { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: 0 var(--crisp-space-3) var(--crisp-space-2); }
1554
+ & .crisp-tabletoolbar-toolbar[data-inline] { padding-bottom: 0; }
1554
1555
  & .crisp-tabletoolbar-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); color: var(--crisp-fg-muted); }
1555
1556
  & .crisp-tabletoolbar-search svg { flex-shrink: 0; }
1556
1557
  & .crisp-tabletoolbar-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); width: 180px; }
@@ -2217,6 +2218,7 @@
2217
2218
  & * { box-sizing: border-box; }
2218
2219
  /* toolbar */
2219
2220
  & .crisp-recordstable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: 0 var(--crisp-space-3) var(--crisp-space-2); }
2221
+ & .crisp-recordstable-toolbar[data-inline] { padding-bottom: 0; }
2220
2222
  & .crisp-recordstable-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); color: var(--crisp-fg-muted); }
2221
2223
  & .crisp-recordstable-search svg { flex-shrink: 0; }
2222
2224
  & .crisp-recordstable-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); width: 180px; }
@@ -517,7 +517,7 @@
517
517
 
518
518
  .crisp-datatable {
519
519
  display: flex; flex-direction: column; gap: var(--crisp-space-2-5); font-family: var(--crisp-font-sans);
520
- & .crisp-datatable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); }
520
+ & .crisp-datatable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); min-height: 49px; border-top: 1px solid var(--crisp-border-subtle); }
521
521
  & .crisp-datatable-search { position: relative; flex: 1; max-width: 280px; display: inline-flex; align-items: center; }
522
522
  & .crisp-datatable-search svg { position: absolute; inset-inline-start: var(--crisp-space-2); color: var(--crisp-fg-muted); pointer-events: none; }
523
523
  & .crisp-datatable-search input { width: 100%; height: 32px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-6); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; transition: box-shadow .15s; }
@@ -1552,6 +1552,7 @@
1552
1552
  display: contents; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1553
1553
  & * { box-sizing: border-box; }
1554
1554
  & .crisp-tabletoolbar-toolbar { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: 0 var(--crisp-space-3) var(--crisp-space-2); }
1555
+ & .crisp-tabletoolbar-toolbar[data-inline] { padding-bottom: 0; }
1555
1556
  & .crisp-tabletoolbar-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); color: var(--crisp-fg-muted); }
1556
1557
  & .crisp-tabletoolbar-search svg { flex-shrink: 0; }
1557
1558
  & .crisp-tabletoolbar-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); width: 180px; }
@@ -2218,6 +2219,7 @@
2218
2219
  & * { box-sizing: border-box; }
2219
2220
  /* toolbar */
2220
2221
  & .crisp-recordstable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: 0 var(--crisp-space-3) var(--crisp-space-2); }
2222
+ & .crisp-recordstable-toolbar[data-inline] { padding-bottom: 0; }
2221
2223
  & .crisp-recordstable-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); color: var(--crisp-fg-muted); }
2222
2224
  & .crisp-recordstable-search svg { flex-shrink: 0; }
2223
2225
  & .crisp-recordstable-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); width: 180px; }
@@ -2,6 +2,7 @@
2
2
  display: contents; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
3
3
  & * { box-sizing: border-box; }
4
4
  & .crisp-tabletoolbar-toolbar { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: 0 var(--crisp-space-3) var(--crisp-space-2); }
5
+ & .crisp-tabletoolbar-toolbar[data-inline] { padding-bottom: 0; }
5
6
  & .crisp-tabletoolbar-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); color: var(--crisp-fg-muted); }
6
7
  & .crisp-tabletoolbar-search svg { flex-shrink: 0; }
7
8
  & .crisp-tabletoolbar-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); width: 180px; }
@@ -3,6 +3,7 @@
3
3
  display: contents; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
4
4
  & * { box-sizing: border-box; }
5
5
  & .crisp-tabletoolbar-toolbar { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: 0 var(--crisp-space-3) var(--crisp-space-2); }
6
+ & .crisp-tabletoolbar-toolbar[data-inline] { padding-bottom: 0; }
6
7
  & .crisp-tabletoolbar-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); color: var(--crisp-fg-muted); }
7
8
  & .crisp-tabletoolbar-search svg { flex-shrink: 0; }
8
9
  & .crisp-tabletoolbar-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); width: 180px; }
@@ -81,6 +81,15 @@ export type TableToolbarProps = Omit<React.ComponentPropsWithoutRef<"div">, "chi
81
81
  sortTriggerLabel?: (sorts: ToolbarSort[], columns: TableToolbarColumn[]) => React.ReactNode;
82
82
  /** Text on the filter trigger. Receives the number of active filters. */
83
83
  filterTriggerLabel?: (activeFilters: number) => React.ReactNode;
84
+ /**
85
+ * Set when nesting `TableToolbar` inside a consumer's own toolbar row
86
+ * (`DataTable`'s `toolbar` slot, e.g.) alongside other same-height
87
+ * controls. Drops the row's own bottom padding, which exists for when
88
+ * `TableToolbar` sits directly above a table with nothing else in its
89
+ * row — that gap isn't the row's to add when something else in the same
90
+ * row already centres on the same 28px line (#398).
91
+ */
92
+ inline?: boolean;
84
93
  className?: string;
85
94
  };
86
95
  /**
@@ -102,5 +111,29 @@ export declare function applySorts<T>(rows: T[], sorts: ToolbarSort[], get?: (ro
102
111
  * composable version, to put above `DataTable` or any table of your own. Pair
103
112
  * it with `applyFilters` / `applySorts` to turn its state into rows.
104
113
  */
105
- export declare function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel, reorderSortLabel, noSortsLabel, addSortLabel, learnSortingLabel, selectConditionLabel, sortFieldLabel, sortDirectionLabel, removeSortLabel, filterFieldLabel, filterFieldPlaceholder, filterValuePlaceholder, ascendingLabel, descendingLabel, sortTriggerLabel, filterTriggerLabel, ...rest }: TableToolbarProps): React.JSX.Element;
114
+ export declare function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel, reorderSortLabel, noSortsLabel, addSortLabel, learnSortingLabel, selectConditionLabel, sortFieldLabel, sortDirectionLabel, removeSortLabel, filterFieldLabel, filterFieldPlaceholder, filterValuePlaceholder, ascendingLabel, descendingLabel, sortTriggerLabel, filterTriggerLabel, inline, ...rest }: TableToolbarProps): React.JSX.Element;
115
+ export type ToolbarToolProps = React.ComponentPropsWithoutRef<"button"> & {
116
+ /** Drawn the way Sort/Filter are once a condition is set (#399). */
117
+ active?: boolean;
118
+ /** The quieter, no-ring variant Sort/Filter never use themselves, for a
119
+ * chip whose "on" state should read as less committed than a real filter. */
120
+ ghost?: boolean;
121
+ };
122
+ /**
123
+ * The Sort/Filter chip's own chrome (`.crisp-tabletoolbar-tool`), standalone
124
+ * (#399) — for a control drawn like Sort/Filter that isn't Sort or Filter (a
125
+ * screen's own quick filter, e.g.). `.crisp-tabletoolbar-tool` alone renders
126
+ * unstyled: the rule lives nested under `TableToolbar`'s own root class, the
127
+ * same way every other rule in its shared chrome does, so it only paints on
128
+ * a descendant of an actual `TableToolbar`. This wraps one in, `display:
129
+ * contents` so it contributes no box of its own — the same technique
130
+ * `TableToolbar`'s own root already uses to disappear from layout.
131
+ */
132
+ export declare const ToolbarTool: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & {
133
+ /** Drawn the way Sort/Filter are once a condition is set (#399). */
134
+ active?: boolean;
135
+ /** The quieter, no-ring variant Sort/Filter never use themselves, for a
136
+ * chip whose "on" state should read as less committed than a real filter. */
137
+ ghost?: boolean;
138
+ } & React.RefAttributes<HTMLButtonElement>>;
106
139
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",